在C语言中,数组名代表数组的首地址,这是由C语言的设计规范决定的。其核心原因可以归结为以下几点:
1. 数组名在大多数情况下会隐式转换为指针
C语言规定,数组名在表达式中使用时,会自动退化为指向数组首元素的指针(即 &array[0]
)。例如:
int arr[5] = {1, 2, 3, 4, 5};
int *p = arr; // 等价于 int *p = &arr[0];
- 这里的
arr
会被编译器隐式转换为&arr[0]
,即第一个元素的地址。
2. 数组名与指针的区别
虽然数组名可以当作指针使用,但它不是指针变量,而是地址常量,不能修改:
- 指针变量(如
int *p
)可以修改其指向(p++
)。 - 数组名是固定地址,不能重新赋值(
arr = new_arr;
会报错)。
示例:
int arr[3] = {10, 20, 30};
int *p = arr;
p++; // 合法,指针可以移动
arr++; // 非法!数组名是常量,不能修改
3. 数组名代表首地址的底层逻辑
- 内存布局:数组在内存中是连续存储的,数组名本质是编译器分配的内存块起始地址的符号。
- 效率优化:直接使用数组名作为指针避免了额外的地址计算,提升访问效率。
内存示例:
地址 | 值
0x1000 | arr[0] (1)
0x1004 | arr[1] (2)
0x1008 | arr[2] (3)
arr
的值就是0x1000
(首元素地址)。
4. 例外情况:数组名不会退化为指针
在以下场景中,数组名不会退化为指针:
使用 sizeof(arr)
返回整个数组的字节大小(而非指针大小):
int arr[5];
printf("%zu", sizeof(arr)); // 输出 20(假设int为4字节)
使用 &arr
时
取数组地址时,得到的是指向整个数组的指针(类型为 int(*)[5]
),而非首元素指针:
int arr[5];
int (*p)[5] = &arr; // p是指向含5个int的数组的指针, p+1跳转大小是5个int大小即20字节。
5. 为什么这样设计?
- 语法简洁性
直接通过arr[i]
访问元素,而不需显式写*(arr + i)
(虽然两者等价)。 - 兼容性
C语言早期设计需要高效操作内存,数组名作为指针符合底层硬件访问模式。 - 历史原因
继承自B语言(C的前身)的数组实现方式。
代码验证示例
#include <stdio.h>
// 数组为啥可以代表数组的首地址
int main()
{
int array[5] = {10, 20, 30, 40, 50};
int *p1 = array; // 数组名指向数组的首地址
for (int i = 0; i < 5; i++)
{
printf("%d: %d --> %d\n", i, *(p1), *(array + i));
p1++;
// array++; //数组名是常量,不可修改
}
printf("\n");
int arr[5] = {10, 20, 30, 40, 50};
printf("arr = %p\n", (void *)arr); // 首元素地址
printf("&arr[0] = %p\n", (void *)&arr[0]); // 同上
printf("&arr = %p\n", (void *)&arr); // 整个数组的地址(值与arr相同,但类型不同)
printf("sizeof(arr) = %zu\n", sizeof(arr)); // 输出12(整个数组大小)
printf("sizeof(&arr[0]) = %zu\n", sizeof(&arr[0])); // 输出8(指针大小)
int(*p)[5] = &arr; // p 指向5个int的数组的指针,p+1跳转大小是5个int大小即20字节。
printf("p =%p\n", (void *)p);
printf("p + 1 =%p\n", (void *)(p + 1));
printf("diff (p + 1 - p) = %ld\n", (void *)(p + 1) - (void *)p);
return 0;
}
输出(假设地址从 0x1000
开始):
0: 10 --> 10
1: 20 --> 20
2: 30 --> 30
3: 40 --> 40
4: 50 --> 50
arr = 0x7ffd0d25eb10
&arr[0] = 0x7ffd0d25eb10
&arr = 0x7ffd0d25eb10
sizeof(arr) = 20
sizeof(&arr[0]) = 8
p =0x7ffd0d25eb10
p + 1 =0x7ffd0d25eb24
diff (p + 1 - p) = 20
总结
行为 |
数组名 |
指针 |
代表地址 |
首元素地址( |
存储的地址值 |
能否修改 |
否(常量) |
是(变量) |
|
整个数组大小(字节) |
指针大小(通常4或8字节) |
|
|
|
简单来说就是,数组名可以表示为指针,但它不是指针变量,而是地址常量,不能修改。
理解这一设计有助于避免以下常见错误:
- 误将数组名当作指针变量修改(如
arr++
)。 - 混淆
arr
和&arr
的类型差异(尤其在函数传参时)。