数组名在多数表达式中退化为指向首元素的指针,但使用sizeof、&运算符或字符串字面量初始化时仍代表整个数组;函数参数中必然退化,导致sizeof失效,需额外传长度或用引用保留类型。

数组名在绝大多数表达式中会自动退化为指向首元素的指针,但不是“总是”——关键看上下文。退化是隐式转换,不是类型改变;一旦退化,就丢失长度信息,这是很多 sizeof 误用和越界访问的根源。
哪些表达式会让数组名退化?
退化发生在**求值上下文**中,即编译器需要一个地址值参与运算时:
-
int arr[5]; int* p = arr;—— 赋值给指针变量,arr退化为&arr[0] -
func(arr);(函数形参声明为int a[]或int* a)—— 传参时必然退化,a在函数内是int*,sizeof(a)返回指针大小 -
arr + 2、arr[3]、*arr—— 指针算术或解引用,arr被当作&arr[0]处理 -
printf("%p", arr);—— 格式化输出地址,arr退化后传入
哪些操作能阻止退化?
这些场景下,arr 仍代表整个数组实体,类型未变:
-
sizeof(arr)—— 返回整个数组字节数(如int[5]是 20),不是指针大小;注意:sizeof是运算符,不是函数 -
&arr—— 得到的是指向整个数组的指针,类型为int(*)[5],不是int*;&arr + 1会跳过全部 5 个元素,而arr + 1只跳过 1 个 -
char str[] = "hello";—— 字符串字面量初始化时,str是数组,不退化;此时sizeof(str)是 6(含 '\0')
为什么函数参数里一定退化?
C++(和 C)标准规定:函数形参中的数组声明(如 void f(int a[10]))等价于 void f(int* a)。编译器不复制数组,只传首地址,所以:
立即学习“C++免费学习笔记(深入)”;
- 无法在函数内用
sizeof(a)获取原数组长度 - 多维数组如
int mat[][10],只有第一维退化,mat类型实际是int(*)[10],第二维尺寸保留 - 若想保留数组类型,必须用引用:
void f(int (&a)[5])—— 此时a不退化,sizeof(a)仍为 20
容易踩的坑:你以为是数组,其实是指针
最常被忽略的是函数内对 sizeof 的误判,以及把数组名当变量修改:
-
arr++编译报错 ——arr是常量地址,不可自增;而p++(p是指针变量)合法 - 模板推导中:
template<typename t> void f(T x) { ... };</typename>传入arr,T推导为int*,不是int[5] - 用
std::begin(arr)和std::end(arr)安全,因为它们特化处理了数组类型;但手写arr + sizeof(arr)/sizeof(*arr)在函数内会崩——因为arr已退化
退化不是 bug,是设计使然;但它要求你始终分清“当前上下文里 arr 是什么类型”。一旦进入函数参数、赋值、运算等求值环境,它就不再是数组,而只是一个地址——这个切换无声无息,却决定着你能做什么、不能做什么。


















