C 语言指针操作:指针算术与数组访问的关系
C 语言中,数组名本质上是指向数组首元素的常量指针。理解指针算术与数组访问的等价性,是掌握 C 语言内存操作的核心。以下指南将通过内存原理解析与代码实操,详细拆解二者之间的转换关系。
1. 理解内存布局与指针步长
指针算术并不简单地改变地址的数值,而是根据数据类型的大小(sizeof(type))进行步进。
定义一个整型数组 int arr[4]。假设 int 类型占用 4 字节,数组首地址为 0x1000。内存分布如下:
arr[0]地址:0x1000arr[1]地址:0x1004arr[2]地址:0x1008arr[3]地址:0x100C
计算指针移动的公式。当执行 ptr + 1 时,CPU 实际执行的地址偏移量计算如下:
$$Address_{new} = Address_{old} + (1 \times sizeof(int))$$
如果 ptr 指向 0x1000,则 ptr + 1 指向 0x1004,而非 0x1001。这保证了指针总是指向数组的下一个元素,而非下一个字节。
2. 对比数组下标与指针解引用
在 C 语言编译器内部,数组下标访问 arr[i] 会被自动转换为指针算术操作。这两种写法在底层生成的机器码是完全一致的。
| 访问方式 | 语法形式 | 指针算术等价形式 | 操作含义 |
|---|---|---|---|
| 数组下标 | arr[i] |
*(arr + i) |
取首地址偏移 i 个元素后的值 |
| 指针下标 | ptr[i] |
*(ptr + i) |
取指针当前地址偏移 i 个元素后的值 |
| 指针解引用 | *(ptr + i) |
arr[i] |
通过地址计算直接访问内存 |
3. 实操步骤:验证指针与数组的等价性
通过以下代码步骤,你可以直观地看到数组名如何作为指针使用,以及指针算术如何遍历数组。
步骤 1:创建一个名为 pointer_array.c 的源文件,并输入以下代码:
#include <stdio.h>
int main() {
int arr[5] = {10, 20, 30, 40, 50};
int *ptr = arr; // 将数组首地址赋值给指针
// 1. 使用数组下标访问
printf("使用数组下标 arr[2]: %d\n", arr[2]);
// 2. 使用指针算术访问
// ptr + 2 计算地址,* 解引用取值
printf("使用指针算术 *(ptr + 2): %d\n", *(ptr + 2));
// 3. 验证指针的步长
printf("ptr 地址: %p\n", (void*)ptr);
printf("ptr + 1 地址: %p\n", (void*)(ptr + 1));
// 4. 使用指针遍历数组
printf("指针遍历数组: ");
for (int i = 0; i < 5; i++) {
// **注意**:这里混合使用了指针下标和算术
printf("%d ", *(ptr + i));
}
printf("\n");
return 0;
}
步骤 2:编译并运行该程序。在终端中执行命令:
gcc pointer_array.c -o pointer_array && ./pointer_array
步骤 3:观察输出结果。你将看到 arr[2] 和 *(ptr + 2) 输出了相同的值 30。同时,注意 ptr 和 ptr + 1 输出的十六进制地址差值为 4(在 64 位系统上 int 通常为 4 字节),证实了指针算术是基于数据类型的步长。
4. 进阶:指针与数组的细微差别
虽然数组名在表达式中常退化为指针,但二者并不完全等同。
区分以下两种操作场景:
-
取地址操作:
- 对数组名取地址
&arr得到的是“整个数组的地址”,其类型是int (*)[5](指向包含 5 个 int 的数组的指针)。 - 对指针取地址
&ptr得到的是指针变量自身在内存中的地址。 - 验证:
&arr + 1会跳过整个数组(20 字节),而arr + 1只跳过一个元素(4 字节)。
- 对数组名取地址
-
赋值操作:
- 数组名是常量指针,禁止修改。
arr = ptr;会导致编译错误。 - 指针是变量,允许修改指向。
ptr = arr;或ptr++;均为合法操作。
- 数组名是常量指针,禁止修改。
牢记:在函数参数传递中,数组参数总是退化为指向首元素的指针。因此,void func(int arr[]) 和 void func(int *arr) 是完全等价的声明。

暂无评论,快来抢沙发吧!