什么是arrays衰减?

什么是arrays的衰减? 与数组指针有任何关系吗?

据说arrays“衰减”成指针。 一个C ++数组声明为int numbers [5]不能被重新指向,即你不能说numbers = 0x5a5aff23 。 更重要的是术语衰减表示types和维度的损失; numbers通过丢失维度信息(计数5)并且types不再是int [5]衰减为int* 。 看看这里没有发生衰变的情况 。

如果你是按值传递一个数组,那么你真正在做的是复制一个指针 – 一个指向数组第一个元素的指针被复制到参数(其types也应该是数组元素types的指针)。 这是由于arrays的衰减性质而起作用的; 一旦衰减, sizeof不再给出完整数组的大小,因为它本质上变成了一个指针。 这就是为什么最好(除其他原因外)通过引用或指针传递。

三种传入数组的方法1

 void by_value(const T* array) // const T array[] means the same void by_pointer(const T (*array)[U]) void by_reference(const T (&array)[U]) 

最后两个将给出适当的sizeof信息,而第一个将不会因为数组参数衰减分配给参数。

1编译时应该知道常量U.

数组基本上与C / C ++中的指针相同,但不完全相同。 一旦你转换一个数组:

 const int a[] = { 2, 3, 5, 7, 11 }; 

到一个指针(它不工作,因此在某些情况下可能会发生意外):

 const int* p = a; 

您将失去sizeof运算符对数组中元素进行计数的能力:

 assert( sizeof(p) != sizeof(a) ); // sizes are not equal 

这种失落的能力被称为“衰变”。

有关更多细节,请查看关于数组衰减的文章 。

这是标准所说的(C99 6.3.2.1/3 – 其他操作数 – 左值,数组和函数指示符):

除了是sizeof运算符的操作数或一元运算符的操作数,或者是用于初始化数组的string字面值以外,具有types为'''的数组的expression式被转换为types为''指针的expression式键入“'指向数组对象的初始元素,而不是一个左值。

这意味着几乎任何时候在expression式中使用数组名称时,它都会自动转换为指向数组中第一项的指针。

请注意,函数名称的作用方式与此类似,但函数指针的使用要less得多,而且使用的方式要特别多,它不会像将数组名称自动转换为指针那样引起混乱。

C ++标准(4.2arrays到指针的转换)将转换需求放到(强调我的):

可以将“NT数组”或“T的未知数组”的左值或右值转换为types“指向T的指针”的右值。

所以转换不必像C中那样总是发生(这可以让函数重载或模板匹配数组types)。

这也是为什么在C中,你应该避免在函数原型/定义中使用数组参数(在我看来 – 我不确定是否有任何一般的协议)。 它们引起混乱,无论如何都是虚构的 – 使用指针参数,混淆可能不会完全消失,但至less参数声明不是说谎。

“衰减”是指将expression式从数组types隐式转换为指针types。 在大多数情况下,当编译器看到一个数组expression式时,它将expression式的types从“T的N元素数组”转换为“指向T的指针”,并将expression式的值设置为数组的第一个元素的地址。 这个规则的例外情况是,当一个数组是一个sizeof或者&操作符的操作数时,或者这个数组是一个string文字在一个声明中被用作一个初始化器。

假设下面的代码:

 char a[80]; strcpy(a, "This is a test"); 

expression式a的types是“80元素char数组”,expression式“This is a test”的types是“char元素的16元素数组”(在C中,在C ++中,string是const char数组)。 但是,在对strcpy()的调用中,两个expression式都不是sizeof&的操作数,所以它们的types被隐式地转换为“指向char的指针”,并且它们的值被设置为每个中的第一个元素的地址。 strcpy()接收的不是数组,而是指针,如其原型所示:

 char *strcpy(char *dest, const char *src); 

这与数组指针不同。 例如:

 char a[80]; char *ptr_to_first_element = a; char (*ptr_to_array)[80] = &a; 

ptr_to_first_elementptr_to_array都具有相同的 ; a的基地址。 但是,它们是不同的types,并且被区别对待,如下所示:

 a[i] == ptr_to_first_element[i] == (*ptr_to_array)[i] != *ptr_to_array[i] != ptr_to_array[i] 

请记住,expression式a[i]被解释为*(a+i) (仅当数组types转换为指针types时才起作用),所以a[i]ptr_to_first_element[i]都是相同的。 expression式(*ptr_to_array)[i]被解释为*(*a+i) 。 根据*ptr_to_array[i] ,expression式*ptr_to_array[i]ptr_to_array[i]可能导致编译器警告或错误; 他们肯定会做错误的事情,如果你期待他们评估a[i]

 sizeof a == sizeof *ptr_to_array == 80 

同样,当一个数组是一个sizeof的操作数时,它不会被转换为一个指针types。

 sizeof *ptr_to_first_element == sizeof (char) == 1 sizeof ptr_to_first_element == sizeof (char *) == whatever the pointer size is on your platform 

ptr_to_first_element是一个简单的char指针。

C中的数组没有任何价值。

无论预期对象的值是什么,但对象是一个数组,其第一个元素的地址将被用作pointer to (type of array elements)typespointer to (type of array elements)

在一个函数中,所有参数都是按值传递的(数​​组也不例外)。 当你在一个函数中传递一个数组时,它会“衰变成一个指针”(原文如此)。 当你将一个数组与另一个数组进行比较时,它又会“衰变成一个指针”(原文如此)。 …

 void foo(int arr[]); 

函数foo需要数组的值。 但是,在C中,数组没有价值! 所以foo获取数组的第一个元素的地址。

 int arr[5]; int *ip = &(arr[1]); if (arr == ip) { /* something; */ } 

在上面的比较中, arr没有任何价值,所以它成为一个指针。 它变成了一个int指针。 该指针可以与variablesip进行比较。

在数组索引语法中,你已经习惯了再次看到arr被“腐蚀成一个指针”

 arr[42]; /* same as *(arr + 42); */ /* same as *(&(arr[0]) + 42); */ 

数组唯一不会衰减为指针的时候是sizeof运算符的操作数,或者是&运算符(“运算符的地址”),或者是用于初始化字符数组的string。

这是当数组烂了,被指向;-)

实际上,如果你想在某个地方传递一个数组,但是指针却被传递了(因为谁会把整个数组传递给你),人们会说穷人的数组会衰减到指针。

数组衰减意味着,当一个数组作为parameter passing给函数时,它的处理方式与(“衰减到”)指针相同。

 void do_something(int *array) { // We don't know how big array is here, because it's decayed to a pointer. printf("%i\n", sizeof(array)); // always prints 4 on a 32-bit machine } int main (int argc, char **argv) { int a[10]; int b[20]; int *c; printf("%i\n", sizeof(a)); //prints 40 on a 32-bit machine printf("%i\n", sizeof(b)); //prints 80 on a 32-bit machine printf("%i\n", sizeof(c)); //prints 4 on a 32-bit machine do_something(a); do_something(b); do_something(c); } 

上述有两个并发症或例外。

首先,在处理C和C ++中的multidimensional array时,只有第一维会丢失。 这是因为数组在内存中是连续放置的,所以编译器必须知道除第一维以外的所有内容,才能计算出该内存块的偏移量。

 void do_something(int array[][10]) { // We don't know how big the first dimension is. } int main(int argc, char *argv[]) { int a[5][10]; int b[20][10]; do_something(a); do_something(b); return 0; } 

其次,在C ++中,可以使用模板来推导出数组的大小。 微软使用这个function来安全的CRTfunction,如strcpy_s的C ++版本,你可以使用类似的技巧来可靠地获得数组中的元素数量 。