5.1 不同类型指针之间的区别和联系
在本书的第1章中就对指针做了初步介绍,同时也交代了不可能用一个小节的内容来讲解指针,因此这里特地用一章的篇幅讲解指针。
前面已初步介绍了不同类型指针之间的区别,但是并没有进行详细分析,那么不同类型的指针之间究竟有什么样的区别和联系呢?
1.不同类型的指针
在讲解不同类型指针的区别和联系之前先来看下面的一段代码,然后对其区别和联系加以总结。
include<stdio.h>
int main(void)
{
char a=1,*pa;
int b=2,*pb;
double c=3,*pc;
pa=&a;
pb=&b;pc=&c;
printf("char型指针pa占用内存大小为:%d字节\n",sizeof(pa));
printf("int型指针pb占用内存大小为:%d字节\n",sizeof(pb));
printf("double型指针pc占用内存大小为:%d字节\n\n",sizeof(pc));
printf("char型指针pa所指向内存区域的大小为:%d字节\n",sizeof(*pa));
printf("int型指针pb所指向内存区域的大小为:%d字节\n",sizeof(*pb));
printf("double型指针pc所指向内存区域的大小为:%d字节\n\n",sizeof(*pc));
printf("char型变量a的地址为:%d\n",pa);
printf("int型变量b的地址为:%d\n",pb);
printf("double型变量c的地址为:%d\n",pc);
return 0;
}
运行结果:
char型指针pa占用内存大小为:4字节
int型指针pb占用内存大小为:4字节
double型指针pc占用内存大小为:4字节
char型指针pa所指向内存区域的大小为:1字节
int型指针pb所指向内存区域的大小为:4字节
double型指针pc所指向内存区域的大小为:8字节
char型变量a的地址为:1245052
int型变量b的地址为:1245044
double型变量c的地址为:1245032
从上面的运行结果可以看出,虽然定义了不同类型的指针,但是它们在内存中都占有4字节的大小,这也进一步验证了前面所讲的内容,指针变量占用内存的大小与它本身的类型无关,而是由使用的计算机决定的。但是不同类型的指针之间也是有区别的,因为不同类型的指针变量所指向内存区域的大小并不相同,可以通过图5-1来了解不同类型的指针在内存中的结构。
图 5-1 不同类型指针的内存结构
结合上面的运行结果和图5-1可以清楚地发现,不同类型的指针在内存中所指向的内存区域的大小并不相同。在第1章中也强调过,不能把指针简单地理解为地址,虽然时常听到“指针就是地址,地址就是指针”这样的说法,但是我们应该将指针与所指向的内存区域结合起来,这样就可以对指针有更加深入的认识,在编程的时候才能够对指针的运用做到心中有数。
2.普通指针和数组指针
普通指针和数组指针之间又有什么样的区别和联系呢?在给出区别之前,先一起来看看下面的一段代码。
include<stdio.h>
int main(void)
{
char a[4];
char(pa)[4],pb;
pa=&a;
pb=&a[0];
printf("char型数组指针pa做占用的内存大小为:%d\n",sizeof(*pa));
printf("char型指针pb做占用的内存大小为:%d\n\n",sizeof(*pb));
printf("pa=%d\tpa+1=%d\n",pa,pa+1);
printf("pb=%d\tpb+1=%d\n",pb,pb+1);
return 0;
}
运行结果:
char型数组指针pa做占用的内存大小为:4
char型指针pb做占用的内存大小为:1
pa=1245052 pa+1=1245056
pb=1245052 pb+1=1245053
在上面的代码中,&a和&a[0]都表示char型数组a的首地址,但是它们的类型并不相同,这是因为&a[0]仅表示数组中一个char型变量的地址,与一个普通的char型变量无异。值得注意的是&a,在此之前也对其进行过相应的分析,先将char a[4]变形为char(&a)[4],这时会清楚地发现&a并不是一个简单的char型指针,而是一个char[4]型的指针,所以接下来定义了一个数组指针char(pa)[4]来存放&a,如果不定义数组指针char(pa)[4],而定义一个charpa,使用pa=&a就会出现“error C2440:'=':cannot convert from'char()[4]'to'char*'”错误,这是因为两者的类型不匹配。如图5-2所示为指针pa、pa+1、pb、pb+1的内存结构。
图 5-2 指针pa、pa+1、pb、pb+1的内存结构
通过上面的运行结果和图5-2发现,数组指针pa和pb所指向的是同一个起始地址,但是由于指针的类型不同,所指向的内存单元的大小也不一样,因此在进行指针运算时得到的结果也不相同。pb到pb+1的变化大小由它所指向的类型决定,由于指针是字符指针,所以变化为一个字节,如果是整型指针,那么变化是4字节;而pa到pa+1的变化大小为4字节,正好为pa指针所指向的类型char*[4],所以在做相应的指针运算时尤其要注意指针所指向的类型。
3.不同类型指针间的强制转换
如果强制转化指针的类型,那会出现什么情况呢?先一起来看看下面的一段代码。
include<stdio.h>
int main(void)
{
int a;
int*pa;
pa=&a;
a=0x12345678;
printf("int型指针pa的值为:%x\n",pa);
printf("char型指针pa的值为:%x\n\n",(char*)pa);
printf("int型指针pa所指内存单元的值为:%x\n",*pa);
printf("char型指针pa所指内存单元的值为:%x\n",(char)pa);
return 0;
}
运行结果:
int型指针pa的值为:12ff7c
char型指针pa的值为:12ff7c
int型指针pa所指内存单元的值为:12345678
char型指针pa所指内存单元的值为:78
从上面的运行结果可以看出。int型指针变量pa中存放变量a的地址,通过(char*)pa将int型指针变量强制转换为char型指针变量,指针的值并没有发生变化,因为任何指针在32位计算机中都用4个字节来表示,所以指针值不会随指针类型的变化而改变。值得注意的是,强制转换后指针所指的内存单元发生了变化,之前pa是整型指针,占用4字节的大小,转换之后变为char型指针,占用1字节的大小,所以接下来采用printf()函数打印出pa指针变化前后所指内存单元的内容时得到的结果不同,如图5-3所示。
通过图5-3可以更加直观地了解指针转换前后pa指针所指向的内存单元的大小。
图 5-3 char型指针pa和int指针pa的内存结构
这里要插入一个知识点,那就是计算机中的大小端模式。所谓小端模式,是数据的低位保存在内存的低地址中,高位保存在内存的高地址中;大端模式就是数据的低位保存在内存的高地址中,而高位保存在内存的低地址中。
很多人对于大小端模式不是太在意,因为大多数情况下在计算机上编程时好像不需要了解它。但是现在很多芯片,如ARM、DSP等,在使用的过程中要尤其注意是大端模式还是小端模式,这直接影响编写代码的方式。如果计算机是大端模式,而我们按照小端模式的方法来编写,那么对于数据的处理就不会按照预期的方式进行,最终得到的也不是想要的结果。在此讲解一下如何判断使用的计算机是小端模式还是大端模式。
include<stdio.h>
int main(void)
{
int a;
int*pa;
pa=&a;
a=0x11223344;
if(0x44==(char)pa)
printf("小端模式!\n");
else
printf("大端模式!\n");
return 0;
}
运行结果:
小端模式!
在上面的代码中,先定义了一个整型变量a,其初始值为0x11223344,同时定义了一个整型指针指向变量a的内存地址,然后通过(char*)pa将整型指针转换为字符指针,如果转换后的字符指针的值为0x44,那么计算机为小端模式,否则为大端模式。这样判断的依据是指针指向的内存单元的起始地址,即低地址,如果转换为字符指针后,其存储单元的值为0x44,那么说明低地址部分保存的是低位,即为小端模式,否则为大端模式。