指针的算术运算
比较指针
使用指针进行数组处理
结合 * 和 ++ 运算符
课堂编码
课堂小测
int a[10], *p;
p = &a[0];
指针可以指向数组元素:
图示:
通过 p 可以访问 a[0]——比如把 5 存进 a[0]:
*p = 5;
如下图:
p 指向数组元素时, 对 p 做指针算术就能访问其他元素
C支持三种(且只有三种)指针运算形式:
指针加上整数
指针减去整数
两个指针相减
int a[10], *p, *q, i;
p = &a[2];
q = p + 3;
p += 6;
p = &a[8];
q = p - 3;
p -= 6;
将整数j添加到指针p会产生一个指向第j个元素的指针, 该元素位于p指向的元素之后.
准确地说: p 指向 a[i], 则 p + j 指向 a[i+j]; p 指向 a[i], 则 p - j 指向 a[i-j]。
假设声明如下:

当一个指针从另一个指针中减去时, 结果是指针之间的距离(数组元素的个数).
p 指向 a[i]、q 指向 a[j], 则 \(p - q = i - j\)。示例:
p = &a[5];
q = &a[1];
i = p - q; /* i is 4 */
i = q - p; /* i is -4 */
对不指向数组元素的指针执行算术运算导致未定义的行为
只有两个指针指向同一个数组时, 把它们相减才有意义
关系运算符(<, <=, >, >=)和判等运算符(==, !=)进行指针比较.
使用关系运算符仅对指向同一数组元素的指针有意义. 比较的结果取决于数组中两个元素的相对位置.
p = &a[5];
q = &a[1];
p <= q; // expression value: 0
p >= q; // expression value: 1
#define N 10
int a[N] = {11, 34, 82, 7, 64, 98, 47, 18, 79, 20};
int sum = 0, *p;
…
for (p = &a[0]; p < &a[N]; p++)
sum += *p;
在第一次迭代结束时:p 指向 a[1],sum 为 11(已加上 a[0])
在第二次迭代结束时:p 指向 a[2],sum 为 45(又加上 a[1])
在第三次迭代结束时:p 指向 a[3],sum 为 127(又加上 a[2])
for 语句中的条件 p < &a[N] 值得特别提及。将地址运算符应用于 a[N] 是合法的,即使此元素不存在。
p = &a[0];
while (p < &a[N])
sum += *p++;
| 表达式 | 意义 |
|---|---|
*p++或*(p++) |
表达式的值是*p在递增之前, 之后自增p |
(*p)++ |
表达式的值为*p在递增之前, 稍后增加*p |
*++p或*(++p) |
先递增p, 表达式的值为*p |
++*p或++(*p) |
先递增*p, 表达式的值为*p |
C程序员经常结合使用*(间接寻址)和++运算符.
修改数组元素后前进到下一个元素的语句: a[i++] = j;
对应的指针版本: *p++ = j;
因为后缀++的优先级高于*, 编译器将其视为 *(p++) = j;
*和++的可能组合见左栏表格.
*(p++) 指针自增(指向下一个对象)
(*p)++ 指向的对象自增(p本身不变)
\*和++最常见的组合是*p++, 在循环中很常用. 上一页的求和 for 循环可以改写成左栏的 while 版.
int contents[100];
int top = 0;
void push(int i) { contents[top++] = i; }
int pop(void) { return contents[--top]; }
int *top_ptr = &contents[0];
void push(int i)
{
if (is_full()) stack_overflow();
else *top_ptr++ = i;
}
int pop(void)
{
if (is_empty()) stack_underflow();
else return *--top_ptr;
}
\*和--运算符的组合方式与\*和++相同.
看一个 * 和 -- 组合的例子——用数组模拟一个栈 (后进先出):
索引版:用整型变量 top 记录 contents 数组的下一个空位——push 把元素存到 top 处,再让 top 后移;pop 先让 top 前移,再返回该处的元素(见左栏)。
指针版:把 top 换成初始指向 contents[0] 的 top_ptr,它同样指向下一个空位,新的 push 和 pop 函数见左栏。
任务:用指针遍历数组,实现两个函数。
int find_sum(int *a, int n):返回数组所有元素之和。
int find_max(int *a, int n):返回数组元素的最大值。
要求:只用指针 *p 访问元素,不使用下标 a[i];循环变量在 for 中自声明。
样例输入:34 82 49 102 7 94 23 11 50 31
样例输出:sum = 483、max = 102
提示:求和让 p 从 a 走到 a + n,每次把 *p 累加;求最大值先把 max 初始化为 *a,再从 a + 1 开始逐个比较。
先想参数:函数需要数组首元素地址 a 和元素个数 n——指针本身不知道数组有多长。
int find_sum(int *a, int n)
{
int sum = 0;
for (int *p = a; p < a + n; p++)
sum += *p;
return sum;
}
int find_max(int *a, int n)
{
int max = *a;
for (int *p = a + 1; p < a + n; p++)
if (*p > max)
max = *p;
return max;
}
#include <stdio.h>
int main(void)
{
int b[10] = {34, 82, 49, 102, 7, 94, 23, 11, 50, 31};
printf("sum = %d\n", find_sum(b, 10)); /* 期望输出: sum = 483 */
printf("max = %d\n", find_max(b, 10)); /* 期望输出: max = 102 */
return 0;
}
两个循环都用 p < a + n 作条件,指针不会走到数组之外;调用时数组名 b 自动作为 &b[0] 传入。
int a[5]; int *p = a; 中 p + 1 表示什么?p + 1 的地址比 p 大多少字节?
p2 - p1 得到的是地址差还是元素个数差?
int a[3] = {1, 2, 3}, *p = a; (*p)++; *++p; 执行后,数组 a 的三个元素各是多少?p 指向哪个元素?
int a[10], *p; for (p = a; p <= a + 10; p++) ... 有什么问题?