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