1. 指针运算的本质与内存视角
指针运算是C语言区别于其他高级语言的核心特性之一。要真正掌握指针运算,必须从计算机内存的基本结构开始理解。在32位系统中,每个内存单元都有一个32位的地址编号,而64位系统则使用64位地址。指针变量本质上就是存储这些内存地址的容器。
当我们声明一个int类型的指针(int *p)时,这个指针的"步长"就被确定为4字节(假设int为32位)。这意味着对指针进行加减运算时,地址值的变化量会自动按照这个步长来调整。例如:
c复制int arr[5] = {10, 20, 30, 40, 50};
int *p = arr; // p指向arr[0]
p++; // 现在p指向arr[1],地址实际增加了4字节
这种自动按类型调整的特性,使得指针运算能够高效地遍历数组等连续内存结构。但同时也带来了许多容易误解的地方,特别是当不同类型的指针混用时。
关键理解:指针运算的单位是"指向类型的大小",而不是字节。这是许多初学者容易混淆的核心概念。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 指针运算的四种基本操作
2.1 指针的加减运算
指针加减整数是最常用的运算,但结果取决于指针类型。例如:
c复制double values[10];
double *ptr = &values[3];
ptr = ptr + 2; // 移动2个double单位(通常16字节),指向values[5]
这里ptr+2不是简单地将地址值加2,而是加2×sizeof(double)。这种自动缩放特性是高效数组处理的基础,但也可能导致意料之外的结果。
2.2 指针的递增递减
++和--运算符同样遵循类型大小规则。前置和后置形式的区别与普通变量相同:
c复制char *cptr = some_address;
cptr++; // 地址增加1字节(char大小为1)
2.3 指针间的减法
同类型指针相减得到的是它们之间的元素个数,而不是字节差:
c复制int *p1 = &arr[2];
int *p2 = &arr[5];
int diff = p2 - p1; // 结果是3,不是12字节
这个特性常用于计算数组元素数量或字符串长度。
2.4 指针的比较
指针可以使用==、!=、<、>等比较运算符,但必须注意:
- 只有指向同一数组或对象的指针比较才有明确定义
- 不同内存区域的指针比较结果是未定义的
3. 数组与指针的等价性解析
数组名在大多数情况下会退化为指向其首元素的指针,这导致数组和指针在使用上有许多相似之处:
c复制int arr[5] = {1,2,3,4,5};
int *p = arr;
// 以下访问方式等价
arr[2] == *(arr+2) == *(p+2) == p[2]
但有三点关键区别:
- sizeof运算符:对数组名得到的是整个数组的大小,对指针得到的是指针本身的大小
- &运算符:&arr得到的是整个数组的地址,&p得到的是指针变量的地址
- 数组名不是左值,不能重新赋值
4. 多级指针的运算规则
多级指针(如int **pp)的运算需要特别注意解引用层级:
c复制int x = 10;
int *p = &x;
int **pp = &p;
// 访问x的三种方式
x == *p == **pp
多级指针常见于:
- 动态二维数组
- 函数参数传递中需要修改指针本身
- 字符串指针数
