考虑代码:
void foo(char a[]){
a++; // works fine, gets compiled
//...
}
现在,请考虑一下:
void foo(){
char a[50];
a++; // Compiler error
//...
}
我听说数组相当于一个常量指针,不能递增,因为它不是左值...
那么为什么第一个代码被编译,是这样的,因为函数的数组参数作为指针传递,即T []转换为T *传递.. 所以,foo(a)传递一个指针。
但它是不是再次转换为T []因为声明为:
void foo(char a[]);
答案 0 :(得分:13)
当您将数组作为参数传递给函数时,它会衰减为指针 所以你在函数体内增加的东西是指针,而不是数组。
答案 1 :(得分:10)
这是从C语言继承而来的一个相当不幸的特征,其中有一个令人讨厌的名字“衰变”。由于C曾经不允许按值传递复合类型,因此他们决定允许程序员将数组指定为函数参数类型,但仅限于美观。数组类型衰减为指针类型,实现了与语言其余部分不同的一种pass-by-reference语义。难看。
回顾(其他人已经说过这个),签名
void foo(char a[]); // asking for trouble
被毫不客气地篡改成
void foo(char *a);
...所有这些都是为了与古代C代码兼容。由于您没有编写古老的C代码,因此不应使用此“功能”。
但是,您可以干净地将引用传递给数组。 C ++要求知道数组的大小:
void foo( char (&a)[ 50 ] );
现在a
无法在函数中修改(编辑:它的内容当然可以 - 你知道我的意思),并且只能传递正确大小的数组。对于其他所有内容,请传递指针或更高级别的类型。
答案 2 :(得分:4)
我听说数组相当于一个常量指针
你可以这样想,但它们并不等同。
数组在传递给函数时衰减到指针,这就是函数内部有效的原因。
仅仅因为签名为void foo(char a[])
不会使a
成为数组。
在函数外部,它只是一个数组,你不能在它上面做指针算术。
答案 3 :(得分:3)
在C ++中,任何类型为“T of array”的函数参数都被调整为“指向T的指针”。试试这段代码:
void foo(char a[]) {}
void foo(char* a) {} //error: redefinition
它们确实是同一个功能。
那么,为什么我们可以将数组参数作为指针参数传递给函数?不是因为数组等效于常量指针,而是因为数组类型可以隐式转换为指针类型的右值。另请注意,转换的结果是rvalue,这就是为什么你不能将operator ++应用于数组,你只能将这个运算符应用于左值。
答案 4 :(得分:1)
当您将数组a []传递给函数时,它会将“a”(地址)的值传递给函数。所以你可以用它作为函数中的指针。如果在函数中声明一个数组,'a'是常量,因为你不能在内存中更改它的地址。
答案 5 :(得分:1)
我听说数组相当于一个常量指针,不能递增,因为它不是左值...
几乎。
数组表达式是不可修改的左值;它可能不是++
或--
等运算符的操作数,也可能不是赋值表达式的目标。这与常量指针(即,声明为T * const
的指针)不同。
当数组表达式是sizeof
或一元{的操作数时,数组表达式将替换为指针表达式,其值是数组的第一个元素的地址(除外) {1}}运算符,或者当数组表达式是用于初始化声明中的另一个数组的字符串文字时。
使用数组参数调用函数时,例如
&
表达式 int a[N];
...
foo(a);
从类型“{元素数组a
”转换为“指向int
的指针”,这个指针值是得到的传递给int
;因此,相应的函数原型应该是
foo
请注意,在函数参数声明的上下文中,void foo (int *arr) {...}
和T a[]
与T a[N]
相同;在所有三种情况下,T *a
都被声明为指向a
的指针。在函数T
中,参数foo
是一个指针表达式,其中是一个可修改的左值,因此它可以被赋值给{并且可能是{的操作数。 {1}}和arr
运营商。
请记住,所有这些转换都在数组上表达式;也就是说,数组标识符或其他表达式引用内存中的数组 object 。不转换数组对象(保存数组值的内存块)。