C++ 什么是数组到指针衰减?
什么是数组到指针衰减?与数组指针有什么关系吗?这是当数组腐烂并被指向时;-) 实际上,如果你想在某个地方传递一个数组,但指针却被传递了(因为谁会为你传递整个数组),人们会说可怜的数组会退化为指针。据说数组会“退化”为指针。声明为“代码> int编号[5 ] 的C++数组不能被重新指向,即不能说<代码>数字= 0x5A5AF223 < /代码>。更重要的是,衰变一词意味着类型和维度的丧失C++ 什么是数组到指针衰减?,c++,c,arrays,pointers,C++,C,Arrays,Pointers,什么是数组到指针衰减?与数组指针有什么关系吗?这是当数组腐烂并被指向时;-) 实际上,如果你想在某个地方传递一个数组,但指针却被传递了(因为谁会为你传递整个数组),人们会说可怜的数组会退化为指针。据说数组会“退化”为指针。声明为“代码> int编号[5 ] 的C++数组不能被重新指向,即不能说数字= 0x5A5AF223 < /代码>。更重要的是,衰变一词意味着类型和维度的丧失numbers通过丢失维度信息(计数5)而衰减为int*,并且类型不再是int[5]。在这里找你 如果按值传递数组,那么
numbers
通过丢失维度信息(计数5)而衰减为int*
,并且类型不再是int[5]
。在这里找你
如果按值传递数组,那么实际上要做的是复制一个指针-指向数组第一个元素的指针被复制到参数(其类型也应该是数组元素类型的指针)。这是由于阵列的衰变性质;一旦衰减,
sizeof
就不再给出完整数组的大小,因为它实际上变成了指针。这就是为什么(除其他原因外)首选通过引用或指针进行传递
在阵列中通过的三种方式1:
最后两个将提供适当的sizeof
信息,而第一个不会,因为数组参数已衰减为指定给参数
1在编译时应该知道常量U。数组基本上与C/C++中的指针相同,但不完全相同。转换阵列后:
const int a[] = { 2, 3, 5, 7, 11 };
插入指针(无需强制转换即可工作,因此在某些情况下可能会意外发生):
您将失去sizeof
运算符对数组中的元素进行计数的能力:
assert( sizeof(p) != sizeof(a) ); // sizes are not equal
这种失去的能力被称为“衰变”
有关更多详细信息,请查看此项。数组衰减意味着,当将数组作为参数传递给函数时,它将被等同于指针(“衰减为”)
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++版本的安全CRT函数,你可以使用类似的技巧来可靠地进行。
数组,在C中,没有任何价值。 如果需要对象的值,但对象是数组,则使用其第一个元素的地址,类型
指针指向(数组元素类型)
在函数中,所有参数都按值传递(数组也不例外)。当你在一个函数中传递一个数组时,它“衰减为指针”(原文如此);当你将一个数组与其他数组进行比较时,它再次“衰减为指针”(原文如此)
函数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运算符的操作数,或&operator(“address of”运算符),或作为用于初始化字符数组的字符串文字时。以下是标准所述(C99 6.3.2.1/3-其他操作数-左值、数组和函数指示符): 除非它是sizeof运算符或一元&运算符的操作数,或是 字符串文字用于初始化数组,类型为“”的数组类型“”的表达式为 已转换为类型为“指向类型的指针”且指向的初始元素的表达式 数组对象,并且不是左值 这意味着只要在表达式中使用数组名,它就会自动转换为指向数组中第一项的指针
int arr[5];
int *ip = &(arr[1]);
if (arr == ip) { /* something; */ }
请注意,函数名的作用方式与此类似,但函数指针的使用要少得多,而且使用的方式更为专门,因此它不会像数组名到指针的自动转换那样引起混乱
C++标准(4.2数组到指针转换)将转换要求放宽到(强调雷):
“NT数组”或“T未知界数组”类型的左值或右值可以转换为右值 类型为“指向T的指针” 因此,转换不必像在C语言中一样发生(这使函数重载或模板与数组类型匹配) 这也是为什么在C中应该避免在函数原型/定义中使用数组参数的原因(在我看来,我不确定是否有任何普遍的一致性)。它们会导致混淆,而且无论如何都是虚构的-使用指针参数,混淆可能不会完全消失,但至少参数声明没有说谎。“衰退”是指表达式从数组类型隐式转换为指针类型。在大多数上下文中,当编译器看到数组表达式时,它会将表达式的类型从“T的N元素数组”转换为“指向T的指针”,并将表达式的值设置为数组第一个元素的地址。此规则的例外情况是,数组是sizeof
或&
运算符的操作数,或者数组是在声明中用作初始值设定项的字符串文字
假设以下代码:
char a[80];
strcpy(a, "This is a test");
表达式a
的类型为“字符的80元素数组”,并且
arr[42];
/* same as *(arr + 42); */
/* same as *(&(arr[0]) + 42); */
char a[80];
strcpy(a, "This is a test");
char *strcpy(char *dest, const char *src);
char a[80];
char *ptr_to_first_element = a;
char (*ptr_to_array)[80] = &a;
a[i] == ptr_to_first_element[i] == (*ptr_to_array)[i] != *ptr_to_array[i] != ptr_to_array[i]
sizeof a == sizeof *ptr_to_array == 80
sizeof *ptr_to_first_element == sizeof (char) == 1
sizeof ptr_to_first_element == sizeof (char *) == whatever the pointer size
is on your platform