10.5 指针操作

可以对指针进行哪些操作?C提供了6种基本的指针操作,下面的程序将具体演示这些操作。为了显示每一个操作结果,程序将打印出指针的值(即指针指向的地址)、指针指向地址中存储的内容,以及指针本身的地址(如果您的编译器不支持%p说明符,那么要想打印出地址,就需要用%u或者%lu)。

程序清单10.13示例了可对指针变量执行的8种基本操作。除了这些操作,你还可以使用关系运算符来比较指针。

广告:个人专属 VPN,独立 IP,无限流量,多机房切换,还可以屏蔽广告和恶意软件,每月最低仅 5 美元

程序清单10.13 ptr_ops.c程序

阅读 ‧ 电子书库

阅读 ‧ 电子书库

输出结果如下:

阅读 ‧ 电子书库

下面的列表描述了可对指针变量执行的基本操作:

● 赋值(assignment)——可以把一个地址赋给指针。通常使用数组名或地址运算符&来进行地址赋值。本例中,把数组urn的起始地址赋给ptrl,该地址是编号为0x0012ff38的内存单元。变量ptr2得到的是数组第3个也即最后一个元素(um[2])的地址。注意:地址应该和指针类型兼容。也就是说,不能把一个double类型的地址赋给一个指向int的指针。C99允许使用类型指派这样做,但是我们不推荐使用这种方法。
● 求值(value-finding)或取值(dereferencing)——运算符*可取出指针指向地址中存储的数值。因此,*ptrl开始为100,即存储在地址0x0012ff38中的值。
● 取指针地址——指针变量同其他变量一样具有地址和数值,使用运算符&可以得到存储指针本身的地址。本例中,ptrl被存储在内存地址0x0012ff34中。该内存单元的内容是0x0012ff38,即urn的地址。
● 将一个整数加给指针——可以使用+运算符来把一个整数加给一个指针,或者把一个指针加给一个正数。两种情况下,这个整数都会和指针所指类型的字节数相乘,然后所得的结果会加到初始地址上。于是,ptr+4的结果等同于&urn[4]。如果相加的结果超出了初始指针所指向的数组的范围,那么这个结果是不确定的,除非超出数组最后一个元素的地址能够确保是有效的。
● 增加指针的值——可以通过一般的加法或增量运算符来增加一个指针的值。对指向某数组元素的指针做增量运算,可以让指针指向该数组的下一个元素。因此,ptrl++运算把ptrl加上数值4(我们系统上的int为4个字节),使ptrl指向urn[1](请参见图10.4)。现在ptrl的值是0x0012ff3c(下一 个数组元素的地址),*ptr的数值为200(urn[1]的值)。请注意ptrl本身的地址仍然是0x0012ff34。别忘了,变量不会因为它的值的变化而移动位置。

阅读 ‧ 电子书库

图10.4 增加一个int指针的值
● 从指针中减去一个整数——可以使用–运算符来从一个指针中减去一个整数。指针必须是第一个操作数,或者是一个指向整数的指针。这个整数都会和指针所指类型的字节数相乘,然后所得的结果会从初始地址中减掉。于是,ptr3-2的结果等同于&urn[2],因为ptr3是指向&urn[4]的。如果相减的结果超出了初始指针所指向的数组的范围,那么这个结果是不确定的,除非超出数组最后一个元素的地址能够确保是有效的。
● 减小指针的值——指针当然也可以做减量运算。本例中,ptr2自减1之后,它将不再指向第三个元素,而是指向第二个数组元素。请注意,你可以使用前缀和后缀形式的增量和减量运算符。对指针ptrl和pt2都指向同一个元素urn[1],直到它们被重置。
● 求差值(Differencing)——可以求出两个指针间的差值。通常对分别指向同一个数组内两个元素的指针求差值,以求出元素之间的距离。差值的单位是相应类型的大小。例如在程序清单10.13的输出中,ptr2-ptrl的值是2,表示指针所指向对象之间的距离为2个int数值大小,而不是2个字节。有效指针差值运算的前提是参加运算的两个指针是指向同一个数组(或是其中之一指向数组后面的第一个地址)。指向两个不同数组的指针之间的差值运算可能会得到一个数值结果,但也可能会导致一个运行时错误。
● 比较——可以使用关系运算符来比较两个指针的值,前提是两个指针具有相同的类型。

注意,这里有两种形式的减法。可以用一个指针减掉另一个指针得到一个整数,也可以从一个指针中减去一个整数得到一个指针。

在进行指针的增量和减量运算时,需要牢记一些注意事项。计算机并不检查指针是否仍然指向某个数组元素。C保证指向数组元素的指针和指向数组后的第一个地址的指针都是有效的。但是如果指针在进行了增量或减量运算后超出了这个范围,后果将是未知的。另外,可以对指向一个数组元素的指针进行取值运算。但不能对指向数组后的第一个地址的指针进行取值运算,尽管这样的指针是合法的。

阅读 ‧ 电子书库 对未初始化的指针取值
使用指针,有一个规则需要特别注意:不能对未初始化的指针取值。例如下面的例子:
阅读 ‧ 电子书库
为什么这样的代码危害极大?这段程序的第二行表示把数值5存储在pt所指向的地址。但是由于pt没有被初始化,因此它的值是随机的,不知道5会被存储到什么位置。这个位置也许对系统危害不大,但也许会覆盖程序数据或者代码,甚至导致程序的崩溃。切记:当创建一个指针时,系统只分配了用来存储指针本身的内存空间,并不分配用来存储数据的内存空间。因此在使用指针之前,必须给它赋予一个已分配的内存地址。比如,可以把一个已存在的变量地址赋给指针(当您使用带有一个指针参量的函数时,就属于这种情况)。或者使用函数malloc()来首先分配内存,该函数将在第12章详细讨论。总之,使用指针时一定要注意,不能对未初始化的指针取值!
阅读 ‧ 电子书库

给定下面的声明:

阅读 ‧ 电子书库

表10.1中是一些合法的和非法的语句:

表10.1 一些合法和非法的语句

 

 

合  法 非  法
ptrl++; urn++;
ptr2=ptrl+2; ptr2=ptr2+ptrl;
ptr2=urn+1; ptr2=urn*ptrl;

这些操作带来很多可能性。C程序员创建了指针数组、函数指针、指向指针的指针数组、指向函数的指针数组等等。但是不要紧张,我们后面的学习重点将放在已经学过的基本使用方式上。指针最基本的功能在于同函数交换信息。从前面所学内容可知,如果需要让被调函数修改调用函数中的变量,就必须使用指针。指针的另一个基本功能是用在处理数组的函数中。下面我们再来看一个同时使用函数和数组的例子。