什么是数组指针衰减?它和数组指针有关系吗?


当前回答

在C语言中,数组没有值。

如果需要对象的值,但对象是数组,则使用其第一个元素的地址,type pointer to(数组元素的类型)。

在函数中,所有参数都是按值传递的(数组也不例外)。当你在函数中传递一个数组时,它“衰减为指针”(原文如此);当你将一个数组与其他东西进行比较时,它再次“衰减为指针”(原文如此);...

void foo(int arr[]);

函数foo期望数组的值。但是,在C语言中,数组没有值!foo得到的是数组第一个元素的地址。

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

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

在数组索引语法中你经常看到,arr被衰减为指针

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

只有当数组是sizeof操作符的操作数,或&操作符('address of'操作符)的操作数,或用作初始化字符数组的字符串字面值时,数组才不会衰减为指针。

其他回答

"Decay" refers to the implicit conversion of an expression from an array type to a pointer type. In most contexts, when the compiler sees an array expression it converts the type of the expression from "N-element array of T" to "pointer to T" and sets the value of the expression to the address of the first element of the array. The exceptions to this rule are when an array is an operand of either the sizeof or & operators, or the array is a string literal being used as an initializer in a declaration.

假设有以下代码:

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

表达式a的类型是“80-element array of char”,表达式“This is a test”的类型是“15-element array of char”(在C语言中;在c++中,字符串字面值是const char数组)。然而,在对strcpy()的调用中,两个表达式都不是sizeof或&的操作数,因此它们的类型被隐式转换为“指向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_element和ptr_to_array都有相同的值;a的基址。但它们是不同的类型,区别对待,如下图所示:

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

请记住,表达式a[i]被解释为*(a+i)(只有在数组类型转换为指针类型时才有效),因此a[i]和ptr_to_first_element[i]的工作原理相同。表达式(*ptr_to_array)[i]被解释为*(*a+i)。表达式*ptr_to_array[i]和ptr_to_array[i]可能会根据上下文导致编译器警告或错误;如果你期望它们的值是a[i],它们肯定会出错。

sizeof a == sizeof *ptr_to_array == 80

同样,当数组是sizeof的操作数时,它不会转换为指针类型。

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指针。

数组衰减意味着,当数组作为参数传递给函数时,它被视为(“衰减为”)指针。

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("%zu\n", sizeof(a)); //prints 40 on a 32-bit machine
    printf("%zu\n", sizeof(b)); //prints 80 on a 32-bit machine
    printf("%zu\n", sizeof(c)); //prints 4 on a 32-bit machine
    do_something(a);
    do_something(b);
    do_something(c);
}

上述情况有两个复杂情况或例外情况。

首先,在C和c++中处理多维数组时,只丢失了第一个维度。这是因为数组在内存中是连续布局的,所以编译器必须知道除第一个维度以外的所有维度,才能计算该内存块的偏移量。

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++中,您可以使用模板来推断数组的大小。微软将此用于c++版本的Secure CRT函数(如strcpy_s),您可以使用类似的技巧可靠地获取数组中的元素数量。

数组是由c语言中的指针自动传递的,其背后的原理只能推测。

int a[5], int *a and int (*a)[5] are all glorified addresses meaning that the compiler treats arithmetic and deference operators on them differently depending on the type, so when they refer to the same address they are not treated the same by the compiler. int a[5] is different to the other 2 in that the address is implicit and does not manifest on the stack or the executable as part of the array itself, it is only used by the compiler to resolve certain arithmetic operations, like taking its address or pointer arithmetic. int a[5] is therefore an array as well as an implicit address, but as soon as you talk about the address itself and place it on the stack, the address itself is no longer an array, and can only be a pointer to an array or a decayed array i.e. a pointer to the first member of the array.

例如,在int (*a)[5]上,对a的第一次解引用将产生int *(因此相同的地址,只是不同的类型,注意不是int a[5]),对a的指针算术,即a+1或*(a+1)将根据5个int数组的大小(这是它所指向的数据类型),第二次解引用将产生int。然而,对于int类型a[5],第一次解引用将产生int类型,指针的算术将与int类型的大小相关。

To a function, you can only pass int * and int (*)[5], and the function casts it to whatever the parameter type is, so within the function you have a choice whether to treat an address that is being passed as a decayed array or a pointer to an array (where the function has to specify the size of the array being passed). If you pass a to a function and a is defined int a[5], then as a resolves to an address, you are passing an address, and an address can only be a pointer type. In the function, the parameter it accesses is then an address on the stack or in a register, which can only be a pointer type and not an array type -- this is because it's an actual address on the stack and is therefore clearly not the array itself.

You lose the size of the array because the type of the parameter, being an address, is a pointer and not an array, which does not have an array size, as can be seen when using sizeof, which works on the type of the value being passed to it. The parameter type int a[5] instead of int *a is allowed but is treated as int * instead of disallowing it outright, though it should be disallowed, because it is misleading, because it makes you think that the size information can be used, but you can only do this by casting it to int (*a)[5], and of course, the function has to specify the size of the array because there is no way to pass the size of the array because the size of the array needs to be a compile-time constant.

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

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

转换为指针(不需要强制转换,因此在某些情况下可能会意外发生):

const int* p = a;

你将失去sizeof操作符计算数组中元素的能力:

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

这种丧失的能力被称为“衰退”。

有关更多详细信息,请参阅这篇关于数组衰减的文章。

据说数组会“衰减”成指针。声明为int numbers[5]的c++数组不能被重指向,即不能说numbers = 0x5a5aff23。更重要的是,衰减一词意味着类型和维度的丧失;数字通过丢失维数信息(计数5)衰减为int*,类型不再是int[5]。看看这里没有发生衰变的情况。

如果你是按值传递一个数组,你实际上是在复制一个指针——指向数组第一个元素的指针被复制到形参(形参的类型也应该是数组元素的类型的指针)。这是由于数组的衰减性质;一旦衰变,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信息,而第一个不会,因为数组参数已经衰减为赋值给形参。

常量U应该在编译时已知。