CodeMap

慎言笃行

导航

[转]关于结构体成员内存对齐的问题

转载声明: www.eternity3.com.
首先要明确什么是内存对齐问题。
假设我们同时声明两个变量:

 程序代码
char a;
short b;


如果我们用&(取地址符号)观察变量a, b的地址的话,我们会发现(以16位CPU为例):
如果a的地址是0x0000,那么b的地址将会是0x0002或者是0x0004。
那么就出现这样一个问题:0x0001这个地址没有被使用,那它干什么去了?
答案就是它确实没被使用。因为CPU每次都是从以2字节(16位CPU)或是4字节(32位CPU)的整数倍的内存地址中读进数据的。
如果变量b的地址是0x0001的话,那么CPU就需要先从0x0000中读取一个short,取它的高8位放入b的低8位,然后再从0x0002中读取下一个short,取它的低8位放入b的高8位中,如图:


这样的话,为了获得b的值,CPU需要进行了两次读操作。
但是如果b的地址为0x0002,如图:


那么CPU只需一次读操作就可以获得b的值了。
所以编译器为了优化代码,往往会根据变量的大小,将其指定到合适的位置,即称为内存对齐。

回到正题,同样的问题也会出现在结构体内部成员变量身上,考虑在32位CPU上处理下面一个结构:

 程序代码
typedef struct {
    unsigned short   bfType;         // 文件类型
    unsigned long    bfSize;         // 文件大小
    unsigned short   bfReserved1;    // 保留位
    unsigned short   bfReserved2;    // 保留位
    unsigned long    bfOffBits;      // 数据偏移位置
}BMPFILEHEADER;


由于32位的CPU是按4字节对齐的,bfType的长度是2字节,bfSize的长度是4字节,这意味着这两个变量的地址将不是连续的,它们之间将会有2字节的空隙。这个就会为fread, fwrite等函数的操作造成困难,导致数据的读写错误,因为我们需要的数据很可能被填充到那个没有使用的空隙里面了。
所以我们需要指示编译器,让它不要按4字节对齐,如下:

 程序代码
#pragma pack(2)
typedef struct {
    unsigned short   bfType;         // 文件类型
    unsigned long    bfSize;         // 文件大小
    unsigned short   bfReserved1;    // 保留位
    unsigned short   bfReserved2;    // 保留位
    unsigned long    bfOffBits;      // 数据偏移位置
}BMPFILEHEADER;
#pragma pack()


其中第一个#pragma pack(2)预处理指令指示编译器,下面的代码所涉及到的变量分别使用2字节对齐,第二个#pragma pack()预处理指令用以恢复编译器的默认对齐宽度。这样便不会出现我们所不希望看到的那些空隙。

文章转载自【Eternity's Site】http://www.eternity3.com.cn/blog/article.asp?id=152

posted on 2007-04-23 13:22  wqlblogger  阅读(968)  评论(1编辑  收藏  举报