一.什么是“数组退化成指针”?
在许多情况下,数组名会退化为指针,即原本代表数组的变量,变成了指向数组第一个元素的指针。这意味着数组名并不总是一个指向整个数组的指针,而是指向数组第一个元素的指针。
二.举例
1.作为函数参数传递时
在函数参数中,数组总是退化为指针。这是因为函数参数的大小是固定的,而数组的大小在编译时并不确定。
#include <stdio.h> void foo(int arr[]) { printf("%zu\n", sizeof(arr)); // 输出指针的大小(4 或 8 字节) } int main() { int arr[10]; foo(arr); }结果:sizeof(arr) 输出的是 指针的大小(4 或 8 字节,取决于32/64位平台)。
分析:传递数组时,arr退化为指针。sizeof(arr)返回的是指针的大小,而不是数组的大小。
void foo(int arr[ ]) 等价于void foo(int *arr)
2.作为函数参数的引用时
即使你使用引用(C++ 中)或者指针(C / C++ 中)传递数组,数组名依然会退化成指针。
#include <iostream> void foo(int* arr) { std::cout << sizeof(arr) << std::endl; // 输出指针大小 } int main() { int arr[10]; foo(arr); // 数组退化为指针 }分析:arr作为参数传递给foo时,退化为指针,即传递给函数的是指向arr[0]的指针。
foo(arr)等价于foo(&a[0]);
3.用作sizeof表达式时
sizeof返回的是数组类型的大小,然而如果在数组名后加上 0,它会退化为指针类型,只看指针类型的大小。
#include <stdio.h> int main() { int arr[10]; printf("%zu\n", sizeof(arr)); // 数组的大小(40字节,假设 int 为4字节) printf("%zu\n", sizeof(arr+0)); // 指针的大小(4 或 8 字节) }分析:
arr:在sizeof(arr)中,arr是一个数组,返回的是数组的总大小(10 * sizeof(int))。arr+0:在sizeof(arr+0)中,arr退化为指针(int*),sizeof返回的是指针的大小
4.在指针运算中
数组名在指针运算中始终退化为指针,因为数组名本质上是指向数组第一个元素的指针。
#include <stdio.h> int main() { int arr[5] = {1, 2, 3, 4, 5}; int *ptr = arr; // 数组退化为指针 printf("%d\n", *(ptr + 2)); // 输出 arr[2] 的值 3 }分析:
arr被赋值给ptr,实际上是指针赋值,这时候arr退化为指向arr[0]的指针。*(ptr + 2)实际上等价于arr[2]。
5.作为函数返回值时(C++)
在 C++ 中,数组返回值也会退化为指针。如果你尝试返回整个数组,编译器会报错,但返回一个指向数组的指针是合法的。
#include <iostream> int* foo() { static int arr[3] = {1, 2, 3}; return arr; // 返回指向数组的指针 } int main() { int* ptr = foo(); std::cout << ptr[1] << std::endl; // 输出 2 }分析:
由于
arr是一个局部数组,返回其指针是合法的(arr退化为指向arr[0]的指针)。注意:数组名退化为指针,因此你得到的是指向数组首元素的指针。
6.数组作为运算中的操作数时
在许多情况下,数组名也会自动退化为指针,尤其是作为运算的操作数时。
#include <stdio.h> int main() { int arr[3] = {1, 2, 3}; printf("%d\n", *(arr + 1)); // 输出 2 }分析:
这里
arr退化为指向arr[0]的指针,arr + 1就是指向arr[1]的指针,*(arr + 1)解引用它,得到 2。
三、总结 & 避免数组传参退化的方法
数组在传参时默认会退化为指向首元素的指针,但这是由形参类型决定的,如果形参不是“数组引用”或“指向数组的指针”,则实参数组一定会发生数组到指针的转换也就是所谓的退化。
简单说:只有当形参显式表示“整个数组类型”时,退化才不会发生。
那什么是形参为“数组引用”或“指向数组的指针”??
1.指向数组的指针(C / C++)
void foo(int (*arr)[10]);int a[10]; foo(&a);实参传的是
&a,类型匹配int (*)[10],没有退化
2.数组引用(C++)
void foo(int (&arr)[10]);int a[10]; foo(a);arr是数组引用,数组身份被完整保留,没有退化
| 情况 | 是否退化 |
|---|---|
foo(arr) | ✅ 实参处发生转换 |
void foo(int arr[]) | ✅ |
void foo(int *arr) | ✅ |
void foo(int (*arr)[N]) | ❌ |
void foo(int (&arr)[N]) | ❌ |
sizeof(arr) | ❌ |
&arr | ❌ |
数组名在函数调用中作为形参时,普通写法一定会退化,只有参数代表整个数组类型,它才能接收整个数组否则都是接收的单个元素,正确不退化的形参写法:void foo( int (* arr )[10] )
| 写法 | 指向 |
|---|---|
int *p | 单个 int |
int (*p)[10] | 整个 int[10] |