在下面的代码中,当doit(x,y)
执行该行时,传递给指针的是什么?的地址x
和y
或值x
和y
?
#includeint 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
数组,因为我们分配给指针,而不是数组(我们不能这样做).所以我们基本上用两个名称x
和y
,对于相同的数组.对传入的数组中的任何更改x
或y
将反映在其中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标准库函数中找到.
首先,作为方便的一个小问题,您可能会发现更容易初始化数组,如下所示:
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
数组,因为我们分配给指针,而不是数组(我们不能这样做).所以我们基本上用两个名称x
和y
,对于相同的数组.对传入的数组中的任何更改x
或y
将反映在其中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标准库函数中找到.