9.2.2 指针变量的引用

请牢记,指针变量中只能存放地址(指针),不要将一个整型变量(或任何其他非地址类型的数据)赋值给一个指针变量。例如,如下范例的写法是不可以的:

和指针变量相关的运算符有两个:

(1)&:取地址运算符,上面已经看到过了。

(2)*:指针运算符(间接访问运算符)。请回忆“*”运算符,在表2.6中有过解释,有两个地方用到了“*”运算符。

· 作为乘法运算符,如3*6。

· 定义指针变量的时候,需要用到“*”运算符,这里的“*”就是指针运算符,如“int*mypoint1;”,但是若“*”不出现在定义指针变量的场合下,而且也不是乘法运算符,那么“*”是什么意思呢?依然是指针运算符,只不过代表的是该指针变量所指向的变量。

看看如下范例:

下面要看一些比较特殊的、容易让人困惑的写法,列举这些特殊的写法的目的,是希望读者以后看到这些特殊的写法时能够认识而不被迷惑。就以上面这段代码为基础来进一步说明:

(1)&*p1。

“&”和“*”在这里一个是取地址运算符,一个是指针运算符,根据表2.6,这两个运算符优先级相同,但结合方向都是从右到左结合,所以要先看*p1,*p1代表的是p1所指向的变量,其实就是a,然后再执行“&”运算,看得出&*p1等价于&a。所以,&*p1其实就是p1。那么&*这两个符号就等于白写了,什么意义都没有。

如果有“p2=&*p1;”,则等价于“p2=p1;”,效果就是让p1也指向p2所指向的内容。

(2)*&a。

先计算&a,也就是a的地址,也就是p1,再进行“*”运算,也就是*p1,上面刚刚说*p1代表p1指向的变量,那就是a,所以*&a其实就是a。那么*&这两个符号就等于白写了,什么意义都没有。

(3)(*p1)++。

*p1就相当于a,这就相当于a++。

(4)*p1++。

因为“++”和“*”同级,但是它们都是从右到左结合,所以*p1++等价于*(p1++),这里的主要问题是要知道p1++是什么意思,这特别重要,因为以后会经常遇到这种写法,那就来讲一讲。

假设现在指针变量p1指向了变量a,如图9.7所示,这里绘制的精确一点,让指针变量p1正好指向1000这个地址的顶部。

前面曾经说过,地址是一个数字,那么地址自加1是什么意思呢?

在谈论地址自加1之前,看看变量自加1。这里探讨的变量a是一个整型变量,那么a++,假设a的值是4,那么a++的值就是5,也就是a这个变量对应的内存中的内容+1。

同理,指针变量自加1,也肯定是这个指针变量中的内容要自加,但是“++”这种本来是自加1的操作,对于指针来讲,就不一定是自加1,自加几,取决于该指针变量所指向的变量类型。如果p1指向的是整型变量,那么p1++,则p1中的内容要加4,因为int是4字节,p1++意味着p1所指向的是a之后的变量,要完整地跳过4字节。所以,p1++后内存内容如图9.8所示。

回到上面的话题,“*(p1++);”的执行会导致p1不再指向a,而是指向a后面的内存。那如果这样写代码:“*(p1++)=5;”,代表什么意思呢?根据自加运算符“++”跟在p1后面代表先用后加的特性,“*(p1++)=5;”的完整含义如下:

①将p1所指向的内容赋值成5,因为开始时p1指向的是a变量,所以相当于a变量的值变成了5。

②让p1自加1,也就是p1++,这导致p1指向了变量a后面的地址,也就是p1指向了地址编号为1004的地址。

图9.7 整型指针变量p1当前指向变量a(p1中保存的是变量a的地址)

图9.8 p1++后的结果(注意p1中保存的内容已经变成1004)

建议读者多写一些测试代码,自己设置断点跟踪调试,加深对指针变量的理解。看看如下范例:

这个范例的意义就是,通过一个中间的指针变量p,交换了两个指针变量pmax和pmin的值,实际上a和b两个变量的值并没有发生任何变化,发生变化的是pmax和pmin的值:pmax原来指向a,现在指向了b,pmin原来指向b,现在指向了a。整个程序的工作原理图如图9.9所示。图9.9(a)所示是在if条件语句执行前的内存示意图,图9.9(b)所示是if条件语句执行后的内存示意图。

图9.9 指针范例工作原理和内存示意图