数据指针可以执行以下几种算术运算:
- 给指针加上整数;
- 从指针减去整数;
- 两个指针相减;
- 比较指针。
函数指针则不一定。
给指针加上整数
这种操作很普遍也很有用。给指针加上一个整数实际上加的数是这个整数和指针数据类型对应字节数的乘积。
各个系统的基本数据类型长度可能不同,正如1.2.1节所述。表1-5显示了大部分系统的常见长度,除非特别指定,本书的示例会使用这里的值。
数据类型 | 长度(字节) |
---|---|
byte |
1 |
char |
1 |
short |
2 |
int |
4 |
long |
8 |
float |
4 |
double |
8 |
为了说明给指针加上整数的效果,我们会使用一个整数数组,如下所示。每次pi
加1,地址就加4。这些变量的内存分配如图1-7所示。指针是用数据类型声明的,以便执行算术运算。这种自动调整指针值的可移植方法之所以可能,前提就是知道数据类型的大小。
int vector[] = {28, 41, 7};
int *pi = vector; // pi: 100
printf("%d\n",*pi); // 显示28
pi += 1; // pi: 104
printf("%d\n",*pi); // 显示41
pi += 1; // pi: 108
printf("%d\n",*pi); // 显示7
如果这里使用数组的名字,返回的只是数组地址,也就是数组第一个元素的地址。
在下面的代码中,我们给指针加3,pi
变量会包含地址112,就是pi
本身的地址:
pi = vector;
pi += 3;
指针指向了自己,这样没什么用,但是说明了在做指针算术运算时要小心。访问超出数组范围的内存很危险,应该避免。没有什么能保证被访问的内存是有效变量,存取无效或无用地址的情况很容易发生。
下面的代码演示了short
和char
类型指针的加法操作:
short s;
short *ps = &s;
char c;
char *pc = &c;
我们假设内存分配如图1-8所示,这里用到的地址以4字节为界。真实的地址可能涉及不同的字节数和不同的字节序。
下面的代码给每个指针加1然后显示内容:
printf("Content of ps before: %d\n",ps);
ps = ps + 1;
printf("Content of ps after: %d\n",ps);
printf("Content of pc before: %d\n",pc);
pc = pc + 1;
printf("Content of pc after: %d\n",pc);
运行后,你应该能得到类似如下的结果:
Content of ps before: 120
Content of ps after: 122
Content of pc before: 128
Address of pc after: 129
ps
指针增加了2,因为short
的长度是2字节。pc
指针增加了1,因为它的数据类型长1字节。这些地址可能没有包含有用的信息。
void
指针和加法
作为扩展,大部分编译器都允许给void
指针做算术运算,这里我们假设void
指针的长度是4。不过,试图给void
指针加1可能导致语法错误。在下面的代码片段中,我们声明指针并试图给它加1:
int num = 5;
void *pv = #
printf("%p\n",pv);
pv = pv+1; //语法警告
下面是警告信息:
warning: pointer of type 'void *' used in arithmetic [-Wpointerarith]
这不是标准C允许的行为,所以编译器发出了警告。不过,pv
包含的地址增加了4字节。
从指针减去整数
就像整数可以和指针相加一样,也能从指针减去整数。减去整数时,地址值会减去数据类型的长度和整数值的乘积。为了演示从指针减去整数的效果,我们使用如下所示的数组。这些变量的内存分配如图1-7所示。
int vector[] = {28, 41, 7};
int *pi = vector + 2; // pi: 108
printf("%d\n",*pi); // 显示7
pi--; // pi: 104
printf("%d\n",*pi); // 显示41
pi--; // pi: 100
printf("%d\n",*pi); // 显示28
pi
每次减1,地址都会减4。
指针相减
一个指针减去另一个指针会得到两个地址的差值。这个差值通常没什么用,但可以判断数组中的元素顺序。
指针之间的差值是它们之间相差的“单位”数,差的符号取决于操作数的顺序。这和指针加法是一样的,加到指针上的是数据的长度。我们把“单位”当做操作数。在下例中,我们声明一个数组和数组元素的指针,然后相减:
int vector[] = {28, 41, 7};
int *p0 = vector;
int *p1 = vector+1;
int *p2 = vector+2;
printf("p2-p0: %d\n",p2-p0); // p2-p0: 2
printf("p2-p1: %d\n",p2-p1); // p2-p1: 1
printf("p0-p1: %d\n",p0-p1); // p0-p1: -1
在第一个printf
语句中,我们看到数组的最后一个和第一个元素的位置相差2,就是说它们的索引值相差2。在最后一个printf
语句中,差值是-1,表示p0
在p1
前面,而且它们紧挨着。图1-9说明了本例中内存的分配情况。
ptrdiff_t
类型表示两个指针差值的可移植方式。在上例中,指针相减的结果以ptrdiff_t
类型返回。因为指针长度可能不同,这个类型简化了处理差值的任务。
不要把这种技术和利用解引操作来做数字相减混淆。在下例中,我们用指针来确定数字中第一个元素和第二个元素中存储的值的差。
printf("*p0-*p1: %d\n",*p0-*p1); // *p0-*p1: -13