sizeof 或 & 中 → 保持"数组"身份(sizeof 得整块大小,&a 是指向整块的指针)&a[0])int a[] 本质就是 int *a一句话:"原地是数组,移动是指针;sizeof/&a 留在原地,传参/运算被搬走。"
数组名在声明它的语境里是整块数组;一旦"移动"(传参、算术、赋值)就退化成指向首元素的指针。C 函数按值传参,传地址是 O(1) 成本、与数组大小无关,这是退化设计的根本原因。
int a[10]; sizeof(a) → 40(整块);void f(int arr[]){ sizeof(arr); } → 4/8(形参已退化成指针)f(a); → a 退化成 &a[0],函数里只能拿指针,不复制数组a + 1 → 指针运算,指向 a[1]&a 不退化,类型是 int(*)[10](指向整个数组),和 a+1 跨的步长完全不同a++ 非法——数组名是常量地址不是指针变量;int *p = a; p++; 才合法| 数组名(声明语境) | 退化成指针(移动语境) | |
|---|---|---|
sizeof |
整块大小(如 40) | 指针大小(4/8) |
| 类型 | int[10] |
int* |
自增 a++ |
非法(常量地址) | 合法(指针变量 p++) |
a vs &a |
&a = 指向整块 int(*)[10] |
a = 指向首元素 int* |
数组名 vs 指针关键对比表:
数组名 a |
指针 p |
|
|---|---|---|
| 类型 | int[10] |
int* |
sizeof |
整块(40) | 指针大小(4/8) |
a++ / p++ |
非法 | 合法 |
a = ... |
非法(常量地址) | 合法(变量) |
形参 int a[] |
等价于 int *a |
就是指针 |
int arr[5]; → sizeof(arr) = 20(整块),sizeof(&arr[0]) = 4/8(int*)void f(int a[10]) → 函数里 sizeof(a) = 4/8;形参 [10] 不限制长度,只是给程序员看的注释——传长度 3 的数组也不报错int a[10]; int *p = a; → a++ 不合法(常量地址)、p++ 合法、sizeof(a) = 40、sizeof(p) = 4/8a[i]/p[i] 下标都能用、算术都跨元素;不像——类型不同、sizeof 不同、数组名不能自增/被赋值(常量地址)、指针是变量uint8_t buf[] 和 uint8_t *buf 完全等价——[] 在形参中直接退化成指针&a 类型是 int(*)[10] 不退化| 相邻概念 | 和本主题的关系 | 孤立理解会犯的错 |
|---|---|---|
| sizeof vs strlen | sizeof 编译期算整块,strlen 运行期数到 \0;数组名退化成指针后 sizeof 就错了 |
传参后还 sizeof 想拿数组长度 |
| 数组指针 vs 指针数组 | int (*p)[N] 是"指向数组的指针"(配 &a),int *p[N] 是"指针数组" |
混用退化和取地址 |
| &a | &a 不退化,类型 int(*)[10],跨一整块 |
以为 &a 和 a 类型一样 |
| 结构体 | 结构体里的数组成员 sizeof 算整块,拷贝结构体 = 拷贝整块数组 | 以为成员数组也会"退化" |
2026-08-03
每主题一页,复习时只翻本目录。