移动网站开发语言WordPress搜索引擎链接提交
在 C 语言中,指针和数组有着非常紧密的联系,但它们本质上是 不同的概念。理解它们的关系是掌握 C 语言内存操作的关键。下面我会从多个角度帮你梳理 指针和数组的直接联系,并解释它们的异同点。
1. 数组和指针的本质区别
| 概念 | 本质 | 存储方式 | 能否修改指向 | 典型用途 |
|---|---|---|---|---|
| 数组(Array) | 一组 连续存储的同类型数据 | 在栈或静态区分配固定大小的内存 | ❌ 不能整体修改(数组名是常量指针) | 存储固定数量的数据 |
| 指针(Pointer) | 一个 变量,存储内存地址 | 可以指向任意内存位置(栈、堆、静态区) | ✅ 可以修改指向(指向不同地址) | 动态内存操作、函数传参 |
关键区别:
- 数组名
arr在大多数情况下会退化为指向首元素的指针(&arr[0]),但它本身不是指针变量(不能重新赋值,如arr = &x是错误的)。 - 指针是一个变量,可以存储任意地址,并且可以修改指向(如
int *p = &x; p = &y;)。
2. 数组和指针的直接联系
(1) 数组名在大多数情况下退化为指针(指向首元素)
int arr[5] = {1, 2, 3, 4, 5};
int *p = arr; // 等价于 int *p = &arr[0];- **
arr 的类型是int[5](数组),但在表达式里(如赋值给指针时),它会 退化为int*(指向arr[0]的指针)**。 - **
arr和&arr[0]是等价的**,都表示数组首元素的地址。
(2) 数组名 arr 和 &arr 的区别
| 表达式 | 类型 | 含义 |
|---|---|---|
arr | int*(退化) | 指向 首元素 arr[0] 的指针 |
&arr | int (*)[5] | 指向 整个数组 arr 的指针(类型是 int[5] 的指针) |
arr + 1 | 移动 1 个 int 大小(4 字节) | 指向 arr[1] |
&arr + 1 | 移动 整个数组大小(5 * 4 = 20 字节) | 指向 arr 的下一个数组(如果有的话) |
示例:
int arr[5] = {1, 2, 3, 4, 5};
printf("%p\n", arr); // 数组首元素地址(等价于 &arr[0])
printf("%p\n", &arr); // 整个数组的地址(值和 arr 相同,但类型不同)
printf("%p\n", arr + 1); // 指向 arr[1](地址 + 4 字节)
printf("%p\n", &arr + 1); // 指向 arr 的下一个位置(地址 + 20 字节)输出:
0x7ffd12345670 (arr)
0x7ffd12345670 (&arr,值相同)
0x7ffd12345674 (arr + 1,+4 字节)
0x7ffd12345684 (&arr + 1,+20 字节)结论:
arr和&arr的 值相同(都是数组的起始地址),但 类型不同:arr是int*(指向int)。&arr是int (*)[5](指向int[5]数组)。
arr + 1和&arr + 1的 步长不同:arr + 1移动 1 个int大小(4 字节)。&arr + 1移动 整个数组大小(5 * 4 = 20 字节)。
(3) 数组访问方式 vs 指针访问方式
数组方式
int arr[3] = {10, 20, 30};
printf("%d\n", arr[1]); // 20(数组下标访问)指针方式
int *p = arr;
printf("%d\n", *(p + 1)); // 20(指针偏移访问)等价关系:
arr[i]等价于*(arr + i)。p[i]等价于*(p + i)。
结论:
- 数组下标访问
arr[i]底层就是指针偏移*(arr + i)。 - 指针可以像数组一样使用
[]运算符(因为[]本质是指针算术)。
(4) 函数传参时数组退化为指针
void printArray(int arr[], int size) { // 实际上 arr 是 int*for (int i = 0; i < size; i++) {printf("%d ", arr[i]); // 等价于 *(arr + i)}
}int main() {int myArr[3] = {1, 2, 3};printArray(myArr, 3); // 数组名退化为指针return 0;
}关键点:
- **函数参数中的
int arr[]实际上等价于int *arr**(编译器不会把数组完整传进去,而是传首地址)。 - 所以 在函数内部无法通过
sizeof(arr)获取数组大小(只能得到指针大小,通常是 4 或 8 字节)。
3. 指针和数组的常见操作对比
| 操作 | 数组方式 | 指针方式 |
|---|---|---|
| 访问第 i 个元素 | arr[i] | *(p + i) 或 p[i] |
| 遍历数组 | for (int i = 0; i < n; i++) { arr[i]; } | for (int *p = arr; p < arr + n; p++) { *p; } |
| 函数传参 | void func(int arr[])(实际是 int*) | void func(int *p) |
| 获取首地址 | arr 或 &arr[0] | p(指针本身) |
| 获取数组大小 | sizeof(arr) / sizeof(arr[0])(仅限数组定义处) | ❌ 无法直接获取(只能手动传大小) |
4. 总结
(1) 指针和数组的联系
✅ 数组名在大多数情况下会退化为指向首元素的指针(如 arr → &arr[0])。
✅ 数组访问 arr[i] 底层就是指针算术 *(arr + i)。
✅ 指针可以像数组一样使用 [] 运算符(如 p[i])。
✅ 函数传参时,数组会退化为指针(无法在函数内获取数组真实大小)。
(2) 指针和数组的区别
❌ 数组名不是指针变量(不能重新赋值,如 arr = &x 是错误的)。
❌ **arr 和 &arr 类型不同(arr 是 int*,&arr 是 int (*)[n])。
❌ 数组在栈/静态区分配固定大小,指针可以指向任意内存(堆、栈、静态区)**。
(3) 关键结论
- 数组名
arr在大多数情况下可以当作指针使用,但它本质不是指针变量。 - 指针更灵活,可以指向任意内存,而数组名是固定的。
- 函数传参时,数组会退化为指针,所以无法在函数内获取数组真实大小(必须额外传
size参数)。
