通过一道试题深入理解指针及数组内存布局

来源:互联网 发布:java int 多少字节 编辑:程序博客网 时间:2024/05/17 01:00
#include <stdio.h>bool bIsLittleEndian(){  union check  {    int i;    char ch;  } c;  c.i = 1; return (c.ch ==1);}int main(int argc, char **argv){   int a[4]={1,2,3,4};   int *ptr1=(int *)(&a+1);   int *ptr2=(int *)((int)a+1);   char *EndianStr=NULL;   if (bIsLittleEndian())   {      EndianStr="The Machine is Little Endian Based...";   }   else   {      EndianStr="Big Endian Machine......";   }   printf("%s---->%x,%x.\n",EndianStr, ptr1[-1],*ptr2);   return 0;}

试问大端和小端模式printf输出的内容是分别什么?

(该试题中采用联合体的内存特点(所有数据成员共享一个内存空间,并且对数据的存取都是从相对于联合体基地址偏移量0处开始)来判断大小端模式,是一种较为高效的方式。)

首先,我们要搞清楚&a和a所代表的含义。

a 相当于&a[0],即数组首元素首地址;

&a 相当于取数组a的首地址。

 

其次,我们得弄明白&a+1和a+1 的区别。

要弄明白这点,需要先知道指针移动的原理。记住:

对一个类型为T的指针的移动,以sizeof(T)为移动单位。

可见,( &a +1 ) 相当于先取数组a的首地址,再加上sizeof(a)的值,即&a+5*sizeof(int), 也就是下一个数组的首地址a[5]。

(int *)( &a +1 ) 强制转换为int *类型,赋给ptr1, ptr1[-1]相当于*(ptr1-1),也就是a[5]往后退4个byte,即a[4];

a指向a[0]的首个字节地址, 经过 ( (int)a+1 )之后, ( (int)a+1 )指向a[0]的第二个字节地址。然后(int *)((int)a+1)把这个第二字节的地址转换为int * 类型赋值给ptr2.

也就是说,*ptr2应该为元素a[0]的第二个字节开始的连续4个byte地址存储的内容。

 

再次,对ptr2指向的具体内容需要考虑大小端模式:

小端存储(little endian,字数据的高字节存放在高地址中,而字数据的低字节存放在低地址中)模式下:

a 的内存存放
地址: 00 01 02 03 04 05 06 07
数据: 01 00 00 00 02 00 00 00
a = 00;
ptr2 = (int*)((int)a + 1);
ptr2 = 01;
所以ptr2指向的内存为
地址: 01 02 03 04
数据: 00 00 00 02
即*ptr2 = 0x2000000;

 

大端存储(big endian,字数据的高字节存放在低地址中,而字数据的低字节存放在高地址中)模式下:

a 的内存存放
地址: 00 01 02 03 04 05 06 07
数据: 00 00 00 01 00 00 00 02
ptr2指向的内存为
地址: 01 02 03 04
数据: 00 00 01 00
*ptr2 = 0x100;

 

0 0
原创粉丝点击