假设有两个指针*p1和*p2,一定要理解语句
*p1=*p2;P1=p1;
的含义。为了说明这个问题,先介绍大端存储和小端存储的概念。
1.大端存储和小端存储
当CPU和内存打交道时,在CPU内部的地址总线和数据总线是和内存的地址总线和数据总线连接在一起的。当一个数从内存中向CPU传送时,有时是以一个字节为单位,有时又以一个字(4个字节)为单位。传过来是放在寄存器里(一般是32个字节),在寄存器中,一个字的表示是右边应该属于低位,左边属于高位,如果寄存器的高位和内存中的高地址相对应,低位和内存的低地址相对应,这就属于小尾端存储。反之则称为大尾端存储。大部分处理器都是小端存储的。
因为16进制的2位正好是1个字节,所以选16进制0x12345678为例,对小尾端存储,低位是0x78,应存入低位地址,所以存入的顺序是
0x78 0x56 0x34 0x12
反之,对于高端存储则为
0x12 0x34 0x56 0x78
图14-1以0x0A0B0C0D为例。
图14-1 图解大端和小端存储
下面利用union的成员共有地址的性质,用一个程序来具体说明小端存储。
【例14.9】演示小端存储的程序。
#include <stdio.h>union s{ int a; char s1[4];}uc;int main( ){ int i=0; uc.a=0x12345678; printf(/"0x%xn/",&uc); for(i=0;i<4;i++) printf(/"0x%x 0x%xn/",&uc.s1[i],uc.s1[i]); return 0; }
声明16进制整数a,它与字符串数组共有地址,a的最低一个字节是0x78,按小端存储,则应存入&uc.s1[0]中,也就是0x4227c0中,最高位地址0x4227c3则应存入0x12,也就是数据的高位。下面的运行结果证明了这一点。其实,可以在调试环境中直接看到这些结果。
0x4227c00x4227c0 0x780x4227c1 0x560x4227c2 0x340x4227c3 0x12
2.指针相等操作
两个指针变量相等,是指它们指向同一个地址。例如:
p2=p1;
不仅使得p1和p2都指向原来p1指向的地址,而且保证*p2=*p1。注意它们的值是原来*p1的值。也就是说,p2放弃自己原来的指向地址及指向地址里存储的值。而语句
*p2=*p1;
的作用是使p2放弃自己原来的指向地址里存储的值,改用p1指向地址里的存储值*p1,但并没有放弃自己的指向地址,即p1和p2仍然保留各自原来的指向。但由此也不能得出p2指向地址里存储的内容就是p1指向地址里存储内容的结论。例如,对整数而言,指向的存储内容是一样的,但对字符数组而言,语句只是使第1个字符是一样的。下面是用两个不同结果说明这一点的例子。
【例14.10】演示整数指针相等操作的程序。
#include <stdio.h>int main( ){ int *p1, *p2; int s1=0x12345678,s2=0x78; p1=&s1;p2=&s2; printf(/"0x%xt0x%xn/",p1,p2); *p2=*p1; printf(/"0x%xt0x%xn/",*p2,*p1); //值相等 printf(/"0x%xt0x%xn/",p1,p2); //地址不变,即不相等 p2=p1; printf(/"0x%xt0x%xn/",*p1,*p2); //值相等 printf(/"0x%xt0x%xn/",p1,p2); //地址也变为相等 return 0;}
这个例子的语句“*p2=*p1;”使用*p1取代*p2,但p2不变。运行结果证明了这一点。
0x12ff74 0x12ff700x12345678 0x123456780x12ff74 0x12ff700x12345678 0x123456780x12ff74 0x12ff74
在有些操作中,常常碰到先执行“*p2=*p1;”,然后又执行一项“p2=p1;”的操作,其目的也是显而易见的。
【例14.11】演示字符指针相等操作的程序。
#include <stdio.h>int main( ){ char *p1, *p2; char s1[16]=/"123456789/",s2[16]=/"GH/"; p1=s1;p2=s2; printf(/"0x%xt0x%xn/",p1,p2); *p2=*p1; printf(/"%stt%sn/",p2,p1); //值并不相等 printf(/"0x%xt0x%xn/",p1,p2); //地址不变,即不相等 p2=p1; printf(/"%st%sn/",p2,p1); //值相等 printf(/"0x%xt0x%xn/",p1,p2); //地址也变为相等 return 0; }
这个例子的语句“*p2=*p1;”并不是用p1指向地址里存储的字符串取代p2指向的字符串,而是使用字符“1”取代原来的第1个字符“G”,运行结果如下:
0x12ff68 0x12ff581H 1234567890x12ff68 0x12ff58123456789 1234567890x12ff68 0x12ff68
这是字符操作特征引起的,所以不能只看表面现象。进一步的分析可以参见16.3.3节的复制字符串的例子。