pho*_*bus 259
据说数组"衰变"成指针.声明为int numbers [5]
无法重新指向的C++数组,即你不能说numbers = 0x5a5aff23
.更重要的是,衰变一词意味着类型和维度的损失; 通过丢失维度信息(计数5)而numbers
衰减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])
Run Code Online (Sandbox Code Playgroud)
最后两个将提供正确的sizeof
信息,而第一个将不会,因为数组参数已经衰减分配给参数.
1在编译时应该知道常量U.
sys*_*USE 96
数组与C/C++中的指针基本相同,但并不完全相同.转换数组后:
const int a[] = { 2, 3, 5, 7, 11 };
Run Code Online (Sandbox Code Playgroud)
到一个指针(没有强制转换,因此在某些情况下会意外发生):
const int* p = a;
Run Code Online (Sandbox Code Playgroud)
你失去了sizeof
操作员计算数组中元素的能力:
assert( sizeof(p) != sizeof(a) ); // sizes are not equal
Run Code Online (Sandbox Code Playgroud)
这种丧失的能力被称为"衰变".
有关更多详细信息,请查看有关数组衰减的文章.
Mic*_*urr 43
这是标准所说的(C99 6.3.2.1/3 - 其他操作数 - 左值,数组和函数指示符):
除非它是sizeof运算符或一元&运算符的操作数,或者是用于初始化数组的字符串文字,否则将类型为''array of type''的表达式转换为类型为''指针的表达式type''指向数组对象的初始元素,而不是左值.
这意味着几乎在表达式中使用数组名称的任何时候,它都会自动转换为指向数组中第1项的指针.
请注意,函数名称以类似的方式起作用,但函数指针的使用要少得多,而且使用方式更加专业,因为它不会引起与将数组名称自动转换为指针一样多的混淆.
C++标准(4.2数组到指针转换)将转换要求放宽到(强调我的):
左值或类型"数组NT的"或"结合的T未知的数组"的右值可以被转换成类型的右值"指针T."
所以转换不会有发生像它几乎总是用C做(这让对数组类型函数重载或模板匹配).
这也是为什么在C中你应该避免在函数原型/定义中使用数组参数(在我看来 - 我不确定是否有任何普遍的协议).它们引起混淆并且无论如何都是虚构的 - 使用指针参数并且混淆可能不会完全消失,但至少参数声明不是说谎.
Joh*_*ode 26
"Decay"是指表达式从数组类型到指针类型的隐式转换.在大多数情况下,当编译器看到数组表达式时,它将表达式的类型从"N元素数组T"转换为"指向T",并将表达式的值设置为数组第一个元素的地址.此规则的例外情况是,数组是sizeof
或&
运算符的操作数,或者数组是在声明中用作初始值设定项的字符串文字.
假设以下代码:
char a[80];
strcpy(a, "This is a test");
Run Code Online (Sandbox Code Playgroud)
表达式a
的类型为"80元素的char数组",表达式"This is a test"的类型为"16元素的char数组"(在C中;在C++中,字符串文字是const char的数组).但是,在调用中strcpy()
,表达式都不是sizeof
或的操作数&
,因此它们的类型被隐式转换为"指向char的指针",并且它们的值被设置为每个中的第一个元素的地址.什么strcpy()
接收不是数组,但是指针,作为其原型看出:
char *strcpy(char *dest, const char *src);
Run Code Online (Sandbox Code Playgroud)
这与数组指针不同.例如:
char a[80];
char *ptr_to_first_element = a;
char (*ptr_to_array)[80] = &a;
Run Code Online (Sandbox Code Playgroud)
双方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]
Run Code Online (Sandbox Code Playgroud)
请记住,表达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
Run Code Online (Sandbox Code Playgroud)
同样,当数组是操作数时sizeof
,它不会转换为指针类型.
sizeof *ptr_to_first_element == sizeof (char) == 1
sizeof ptr_to_first_element == sizeof (char *) == whatever the pointer size
is on your platform
Run Code Online (Sandbox Code Playgroud)
ptr_to_first_element
是一个指向char的简单指针.
pmg*_*pmg 13
C中的数组没有任何价值.
在期望对象的值但对象是数组的任何地方,使用其第一个元素的地址来代替类型pointer to (type of array elements)
.
在函数中,所有参数都按值传递(数组也不例外).当你在一个函数中传递一个数组时,它"衰变成一个指针"(原文如此); 当你将一个数组与其他数据进行比较时,它再次"衰变成一个指针"(原文如此); ...
void foo(int arr[]);
Run Code Online (Sandbox Code Playgroud)
函数foo期望数组的值.但是,在C中,数组没有价值!因此,foo
获取数组的第一个元素的地址.
int arr[5];
int *ip = &(arr[1]);
if (arr == ip) { /* something; */ }
Run Code Online (Sandbox Code Playgroud)
在上面的比较中,arr
没有值,所以它变成了一个指针.它成为指向int的指针.该指针可以与变量进行比较ip
.
在数组索引语法中,您习惯于再次看到arr''衰变为指针'
arr[42];
/* same as *(arr + 42); */
/* same as *(&(arr[0]) + 42); */
Run Code Online (Sandbox Code Playgroud)
数组不会衰减为指针的唯一情况是它是sizeof运算符的操作数,或者是&运算符('运算符的地址),或者是用于初始化字符数组的字符串文字.
试试这个代码
void f(double a[10]) {
printf("in function: %d", sizeof(a));
printf("pointer size: %d\n", sizeof(double *));
}
int main() {
double a[10];
printf("in main: %d", sizeof(a));
f(a);
}
Run Code Online (Sandbox Code Playgroud)
你会看到函数内部数组的大小不等于main中数组的大小,但它等于指针的大小。
您可能听说过“数组是指针”,但是,这并不完全正确(sizeof
内部main
打印正确的大小)。然而,当传递时,数组衰减为指针。也就是说,无论语法显示什么,您实际上传递了一个指针,并且该函数实际上接收了一个指针。
在这种情况下,void f(double a[10]
编译器将定义隐式转换为void f(double *a)
. 您可以等效地将函数参数直接声明为*a
. 您甚至可以编写a[100]
or a[1]
,而不是a[10]
,因为它实际上从未以这种方式编译(但是,您不应该这样做,显然,它会让读者感到困惑)。
数组衰减意味着,当数组作为参数传递给函数时,它的处理方式与指针相同(“衰减到”)。
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);
}
Run Code Online (Sandbox Code Playgroud)
上述情况有两个复杂或例外情况。
首先,在 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;
}
Run Code Online (Sandbox Code Playgroud)
其次,在 C++ 中,您可以使用模板来推断数组的大小。Microsoft 将其用于 C++ 版本的 Secure CRT 函数(例如strcpy_s),并且您可以使用类似的技巧来可靠地获取数组中的元素数量。