当前位置:  开发笔记 > 编程语言 > 正文

帮助C中的数组参数

如何解决《帮助C中的数组参数》经验,为你挑选了1个好方法。

在下面的代码中,当doit(x,y)执行该行时,传递给指针的是什么?的地址xy或值xy

#include 

int doit(int x[], int y[]) {
   x = y;
   x[0] = 5;
   y[2] = 10;
}

int main(void) {
   int x[2];
   int y[2];

   x[0] = 1;
   x[1] = 2;
   y[0] = 3;
   y[1] = 4;

   doit(x, y);
   printf("%d %d %d %d", x[0], x[1], y[0], y[1]);
}

Chris Lutz.. 5

首先,作为方便的一个小问题,您可能会发现更容易初始化数组,如下所示:

int x[2] = { 1, 2 };
int y[2] = { 3, 4 };

请注意,这仅适用于初始化.我们不能这样做:

int x[2];
x = { 1, 2 };

这是一个数组初始化.我们不是"分配"数组,因为数组不是左值,但我们仍然可以初始化它.请注意,我们未填写的任何值都将初始化为零:

int lots[100] = { 1 };

现在lots是一个包含100个元素的数组,lots[0]是1,并且lots[1]through lots[99]都是0.我们保证这些值,因为我们初始化了数组.如果我们刚刚做了:

int lots[100];

然后我们声明了,但没有初始化我们的数组,所以它像任何其他局部变量一样保存垃圾.一个常见的习语是:

int lots[100] = { 0 };

要将我们的数组初始化为全零 - 我们声明的一个值为0,其他值自动归零.

其次,为了解决您的实际问题,让我们来看看doit():

int doit(int x[], int y[]) {

这是非常基本的 - 它声明了一个带两个数组的函数.但是你不能将数组传递给C中的函数,或者从C中的函数返回它们,那么这究竟意味着什么呢?

int doit(int *x, int *y) {

在C中,所有"数组"(当传递给函数时)是指向该数组的第一个元素的指针.这就是为什么数组是零索引的原因.要访问数组的第一个元素,我们取消引用指针.要访问第二个元素,我们添加sizeof(array type)到指针,并取消引用:

x[0] == *x
x[1] == *(x + 1)
// and so on

一个常见的产品如下:

x[2] == *(x + 2) == *(2 + x) == 2[x]

虽然不常用,但它仍然很酷.但是不要在实际代码中使用它.这绝对酷.

无论如何,所以我们的数组的地址(以及指向我们数组的第一个元素的指针)被传递给我们的doit()函数.接下来发生什么:

    x = y;

这就是说"告诉我们的本地x指针指向y.它不会改变原始x数组,因为我们分配给指针,而不是数组(我们不能这样做).所以我们基本上用两个名称xy,对于相同的数组.对传入的数组中的任何更改xy将反映在其中y.

    x[0] = 5;
    y[2] = 10;

这是非常基本的.我们将数组的第一个元素设置为5,将第三个元素设置为10.这里没有问题,假设传入的数组y至少有三个元素(提示不祥的音乐).

}

实际上,这是其中一个更大的问题doit().我们是如何申报的doit()

int doit(int x[], int y[])

所以doit()返回一个int,但我们没有return声明!虽然一些编译器可能在不同程度上接受这一点,更好的做法是要么返回一个值,或改变的返回类型doit()void(我怀疑是你想要的),因此它不会返回任何东西.如果你的函数返回void,你可以省略最后的return语句,或者你可以明确地说return;没有参数,因为你什么也没有返回.(别担心,这个怪物差不多完了.)

如你所知,这里最大的问题是我们不能保证我们传递函数的数组将有三个元素.我们甚至不能保证他们会成为阵列.我们可以这样称呼它:

int i = 10;
doit(&i, &i);

这是合法的.C语言不会检查以确保传递函数的数组足够大,也不会为您提供任何用于检查数组大小的内置工具.这不起作用:

size_t array_size(int a[]) {
    return sizeof(a) / sizeof(a[0]);
}

它不起作用,因为它在编译器级别重写,如下所示:

size_t array_size(int *a) {
    return sizeof(a) / sizeof(a[0]);
}

并且sizeof(int *)不是数组的大小,而是指向数组的指针的大小(指针只是内存中的数字地址,通常是计算机字).因此,虽然y[2]doit()函数中修改是合法的,但是传入的数组main()没有足够的元素,因此我们得到了未定义的行为.编译器可以做任何事情 - 它可以覆盖x,或覆盖其他一些局部变量,或覆盖你的代码数据,或做任何想做的事情(经典的例子让恶魔飞出你的鼻子)仍然是一个有效的C编译器.这是C的危险 - 它不会检查以确保你遵守规则.这样做的原因是它意味着是一种快速语言,能够生成高效的代码,并且检查你是否遵守规则使你的程序变慢.因此,如果您想确保遵守规则,您必须自己检查规则:

void doit(int y[], size_t len) {
    y[0] = 5;
    if(len > 2)
        y[2] = 10;
    else
        y[len] = 10;
}

然后叫它:

#define SIZE 2
int y[SIZE] = { 3, 4 };
doit(y, SIZE);

现在我们可以doit()安全地呼叫,即使是在小型阵列上.这种将数组长度作为单独的参数传递给函数的模式在C中非常常见,并且可以在大多数处理数组和指针的C标准库函数中找到.



1> Chris Lutz..:

首先,作为方便的一个小问题,您可能会发现更容易初始化数组,如下所示:

int x[2] = { 1, 2 };
int y[2] = { 3, 4 };

请注意,这仅适用于初始化.我们不能这样做:

int x[2];
x = { 1, 2 };

这是一个数组初始化.我们不是"分配"数组,因为数组不是左值,但我们仍然可以初始化它.请注意,我们未填写的任何值都将初始化为零:

int lots[100] = { 1 };

现在lots是一个包含100个元素的数组,lots[0]是1,并且lots[1]through lots[99]都是0.我们保证这些值,因为我们初始化了数组.如果我们刚刚做了:

int lots[100];

然后我们声明了,但没有初始化我们的数组,所以它像任何其他局部变量一样保存垃圾.一个常见的习语是:

int lots[100] = { 0 };

要将我们的数组初始化为全零 - 我们声明的一个值为0,其他值自动归零.

其次,为了解决您的实际问题,让我们来看看doit():

int doit(int x[], int y[]) {

这是非常基本的 - 它声明了一个带两个数组的函数.但是你不能将数组传递给C中的函数,或者从C中的函数返回它们,那么这究竟意味着什么呢?

int doit(int *x, int *y) {

在C中,所有"数组"(当传递给函数时)是指向该数组的第一个元素的指针.这就是为什么数组是零索引的原因.要访问数组的第一个元素,我们取消引用指针.要访问第二个元素,我们添加sizeof(array type)到指针,并取消引用:

x[0] == *x
x[1] == *(x + 1)
// and so on

一个常见的产品如下:

x[2] == *(x + 2) == *(2 + x) == 2[x]

虽然不常用,但它仍然很酷.但是不要在实际代码中使用它.这绝对酷.

无论如何,所以我们的数组的地址(以及指向我们数组的第一个元素的指针)被传递给我们的doit()函数.接下来发生什么:

    x = y;

这就是说"告诉我们的本地x指针指向y.它不会改变原始x数组,因为我们分配给指针,而不是数组(我们不能这样做).所以我们基本上用两个名称xy,对于相同的数组.对传入的数组中的任何更改xy将反映在其中y.

    x[0] = 5;
    y[2] = 10;

这是非常基本的.我们将数组的第一个元素设置为5,将第三个元素设置为10.这里没有问题,假设传入的数组y至少有三个元素(提示不祥的音乐).

}

实际上,这是其中一个更大的问题doit().我们是如何申报的doit()

int doit(int x[], int y[])

所以doit()返回一个int,但我们没有return声明!虽然一些编译器可能在不同程度上接受这一点,更好的做法是要么返回一个值,或改变的返回类型doit()void(我怀疑是你想要的),因此它不会返回任何东西.如果你的函数返回void,你可以省略最后的return语句,或者你可以明确地说return;没有参数,因为你什么也没有返回.(别担心,这个怪物差不多完了.)

如你所知,这里最大的问题是我们不能保证我们传递函数的数组将有三个元素.我们甚至不能保证他们会成为阵列.我们可以这样称呼它:

int i = 10;
doit(&i, &i);

这是合法的.C语言不会检查以确保传递函数的数组足够大,也不会为您提供任何用于检查数组大小的内置工具.这不起作用:

size_t array_size(int a[]) {
    return sizeof(a) / sizeof(a[0]);
}

它不起作用,因为它在编译器级别重写,如下所示:

size_t array_size(int *a) {
    return sizeof(a) / sizeof(a[0]);
}

并且sizeof(int *)不是数组的大小,而是指向数组的指针的大小(指针只是内存中的数字地址,通常是计算机字).因此,虽然y[2]doit()函数中修改是合法的,但是传入的数组main()没有足够的元素,因此我们得到了未定义的行为.编译器可以做任何事情 - 它可以覆盖x,或覆盖其他一些局部变量,或覆盖你的代码数据,或做任何想做的事情(经典的例子让恶魔飞出你的鼻子)仍然是一个有效的C编译器.这是C的危险 - 它不会检查以确保你遵守规则.这样做的原因是它意味着是一种快速语言,能够生成高效的代码,并且检查你是否遵守规则使你的程序变慢.因此,如果您想确保遵守规则,您必须自己检查规则:

void doit(int y[], size_t len) {
    y[0] = 5;
    if(len > 2)
        y[2] = 10;
    else
        y[len] = 10;
}

然后叫它:

#define SIZE 2
int y[SIZE] = { 3, 4 };
doit(y, SIZE);

现在我们可以doit()安全地呼叫,即使是在小型阵列上.这种将数组长度作为单独的参数传递给函数的模式在C中非常常见,并且可以在大多数处理数组和指针的C标准库函数中找到.

推荐阅读
贾志军
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有