当我们将一个数组元素传递给一个函数时,它被视为一个普通变量,被调用函数创建一个实际参数的副本并对其进行操作。正式参数中的任何更改都不会影响实际参数。
但是当我们传递整个数组时,情况并非如此。在这种情况下,它(称为函数)可以访问实际参数,并且正式参数中的任何更改都会影响实际参数。为什么会这样?
答案 0 :(得分:17)
数组作为指向元素的指针传递。
如果我写:
void doStuff(int *ptr)
{
ptr[1] = 5;
}
int main()
{
int arr[] = {0, 1, 2};
doStuff(arr);
printf("%d %d %d\n", arr[0], arr[1], arr[2]);
}
输出为“0 5 2”。那是因为C通过值传递任何实际参数。该参数是指向int的指针。所以指向int的指针按值传递。因此,doStuff在main的堆栈帧中获取指向内存的指针的副本。当它用ptr[1]
取消引用该指针时,它跟随指向main的内存并在那里修改数组。
C只能按值传递,但它“浅”。如果你要求它传递一个int *,它将传递一个int *。它只复制指针的值,而不是它指向的任何值。
如果你想让doStuff获得它自己的数组副本,或者像其他人建议的那样将数组包装在结构中,或者使用memcpy手动深度复制数组,如下所示:
void doStuff(int *ptr, int nElems)
{
int myCopyOfTheArray[nElems];
memcpy(myCopyOfTheArray, ptr, sizeof(int) * nElems);
/* do stuff with the local copy */
}
与使用结构不同,如果仅在运行时知道nElems,则memcpy方法有效。
答案 1 :(得分:8)
数组不能通过C中的值传递。它们被降级为指针。因此被调用函数看到一个指向数组的指针(通过引用传递)并对其进行操作。如果要创建副本,则必须明确地执行此操作,或者将数组放在结构中(可以通过值传递给函数。)
答案 2 :(得分:5)
通常,制作整个阵列的副本是昂贵的。回到C首次创建的日子,它可能很容易耗尽机器资源(特别是堆栈)。
如果你真的想要传递数组,请将其包装在一个结构中:
struct wrap { int array[100]; };
int somefunc(struct wrap large) { ... }
void anotherfunc(void)
{
struct wrap a;
...add data to array...
printf("%d\n", somefunc(a));
}
答案 3 :(得分:3)
“按引用传递”是C中的红色鲱鱼。函数的所有参数都只是“按值传递”。对于数组,您以指针的形式传递第一个元素的地址。然后,您可以使用此指针指向所需的存储位置&读或写值。
答案 4 :(得分:3)
我还没有真正看到任何答案,但尚未涵盖整个问题。从我看到的情况来看,问题似乎是这样的:
给出以下代码:
int a[5];
foo(a);
bar(a[0]);
为什么foo
可以在bar
只收到传入的值的副本时操纵原始值?
这是因为使用了数组表示法:[]
运算符取消引用它在数组中引用的项,并传入该位置的值。
所以
int a[5];
bar(a[0]);
不同于:
int* a;
bar(a);
如果要传递对数组中特定项的引用,则需要使用&
运算符:
bar(&a[5]);
答案 5 :(得分:2)
6.3.2.1左值,数组和函数指示符
...
3除非它是 sizeof 运算符或一元& 运算符的操作数,或者是用于初始化数组的字符串文字,否则表达式具有“array”类型类型“转换为类型为”的表达式“指向类型”的指针指向数组对象的初始元素而不是左值。如果数组对象具有寄存器存储类,则行为未定义。
假设以下代码段:
int a[10];
...
foo(a);
在对foo
的调用中,表达式 a
的类型是“{element = 1}}的10个元素数组”。但是,由于表达式不是int
或sizeof
运算符的操作数,并且因为它不是用于初始化声明中的另一个数组的字符串文字,所以它的类型是隐式转换的(从“{元素&
的10元素数组”到“int
”的指针“衰减”,其值将是数组中第一个元素的地址(即int
) 。
因此,&a[0]
收到的是指向foo
的指针,而不是数组。取消引用或下标此指针允许您更改数组的值。
这是C语言的一个特性;数组不是第一类对象。实际上,在大多数情况下(包括下标),数组表达式将被转换为指针类型。
我一直强调单词 expression 来区分实际的数组对象(永远是一个数组类型)和任何引用代码中的那个对象,可以转换为指针类型。
答案 6 :(得分:1)
C中的数组可以被视为指向数组第一个元素的指针。指针按值传递(您无法修改传入的值),但您可以取消引用它并修改它指向的内存。
答案 7 :(得分:1)
因为当你将整个数组传递给函数时你传递一个指向该数组的指针,这意味着你给函数一个在这个数组所在的内存中的位置。因此,当您在函数中更改数组时,您也在更改原始数组。