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 不同类型指针之间的区别和联系 - 图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.1 不同类型指针之间的区别和联系 - 图2

图 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.1 不同类型指针之间的区别和联系 - 图3

图 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,那么说明低地址部分保存的是低位,即为小端模式,否则为大端模式。