网站建设公司业务跟不上,中山小程序开发,传媒免费网站建设,绵阳新区大建设结构体字节对齐在用sizeof运算符求算某结构体所占空间时#xff0c;并不是简单地将结构体中所有元素各自占的空间相加#xff0c;这里涉及到内存字节对齐的问题。从理论上讲#xff0c;对于任何变量的访问都可以从任何地址开始访问#xff0c;但是事实上不是如此#xff0…结构体字节对齐在用sizeof运算符求算某结构体所占空间时并不是简单地将结构体中所有元素各自占的空间相加这里涉及到内存字节对齐的问题。从理论上讲对于任何变量的访问都可以从任何地址开始访问但是事实上不是如此实际上访问特定类型的变量只能在特定的地址访问这就需要各个变量在空间上按一定的规则排列而不是简单地顺序排列这就是内存对齐。内存对齐的原因1)某些平台只能在特定的地址处访问特定类型的数据2)提高存取数据的速度。比如有的平台每次都是从偶地址处读取数据对于一个int型的变量若从偶地址单元处存放则只需一个读取周期即可读取该变量但是若从奇地址单元处存放则需要2个读取周期读取该变量。在C99标准中对于内存对齐的细节没有作过多的描述具体的实现交由编译器去处理所以在不同的编译环境下内存对齐可能略有不同但是对齐的最基本原则是一致的对于结构体的字节对齐主要有下面两点1)结构体每个成员相对结构体首地址的偏移量(offset)是对齐参数的整数倍如有需要会在成员之间填充字节。编译器在为结构体成员开辟空间时首先检查预开辟空间的地址相对于结构体首地址的偏移量是否为对齐参数的整数倍若是则存放该成员若不是则填充若干字节以达到整数倍的要求。2)结构体变量所占空间的大小是对齐参数大小的整数倍。如有需要会在最后一个成员末尾填充若干字节使得所占空间大小是对齐参数大小的整数倍。注意在看这两条原则之前先了解一下对齐参数这个概念。对于每个变量它自身有对齐参数这个自身对齐参数在不同编译环境下不同。下面列举的是两种最常见的编译环境下各种类型变量的自身对齐参数从上面可以发现在windows(32)/VC6.0下各种类型的变量的自身对齐参数就是该类型变量所占字节数的大小而在linux(32)/GCC下double类型的变量自身对齐参数是4是因为linux(32)/GCC下如果该类型变量的长度没有超过CPU的字长则以该类型变量的长度作为自身对齐参数如果该类型变量的长度超过CPU字长则自身对齐参数为CPU字长而32位系统其CPU字长是4所以linux(32)/GCC下double类型的变量自身对齐参数是4如果是在Linux(64)下则double类型的自身对齐参数是8。除了变量的自身对齐参数外还有一个对齐参数就是每个编译器默认的对齐参数#pragma pack(n)这个值可以通过代码去设定如果没有设定则取系统的默认值。在windows(32)/VC6.0下n的取值可以为1、2、4、8默认情况下为8。在linux(32)/GCC下n的取值只能为1、2、4默认情况下为4。注意像DEV-CPP、MinGW等在windows下n的取值和VC的相同。了解了这2个概念之后可以理解上面2条原则了。对于第一条原则每个变量相对于结构体的首地址的偏移量必须是对齐参数的整数倍这句话中的对齐参数是取每个变量自身对齐参数和系统默认对齐参数#pragma pack(n)中较小的一个。举个简单的例子比如在结构体A中有变量int aa的自身对齐参数为4(环境为windows/vc)而VC默认的对齐参数为8取较小者则对于a它相对于结构体A的起始地址的偏移量必须是4的倍数。对于第二条原则结构体变量所占空间的大小是对齐参数的整数倍。这句话中的对齐参数有点复杂它是取结构体中所有变量的对齐参数的最大值和系统默认对齐参数#pragma pack(n)比较较小者作为对齐参数。举个例子假如在结构体A中先后定义了两个变量int a;double b;对于变量a它的自身对齐参数为4而#pragma pack(n)值默认为8则a的对齐参数为4b的自身对齐参数为8而#pragma pack(n)的默认值为8则b的对齐参数为8。由于a的最终对齐参数为4b的最终对齐参数为8那么两者较大者是8然后再拿8和#pragma pack(n)作比较取较小者作为对齐参数也就是8即意味着结构体最终的大小必须能被8整除。下面是测试例子注意以下例子的测试结果均在windows(32)/VC下测试的其默认对齐参数为8复制代码/测试sizeof运算符 2011.10.1/includeusing namespace std;//#pragma pack(4) //设置4字节对齐//#pragma pack() //取消4字节对齐typedef struct node1{int a;char b;short c;}S1;typedef struct node2{char a;int b;short c;}S2;typedef struct node3{int a;short b;static int c;}S3;typedef struct node4{bool a;S1 s1;short b;}S4;typedef struct node5{bool a;S1 s1;double b;int c;}S5;int main(int argc, char *argv[]){coutS1 s1;S2 s2;S3 s3;S4 s4;S5 s5;coutreturn 0;}复制代码下面解释一下其中的几个结构体字节分配的情况比如对于node2typedef struct node2{char a;int b;short c;}S2;sizeof(S2)12;对于变量a它的自身对齐参数为1#pragma pack(n)默认值为8则最终a的对齐参数为1为其分配1字节的空间它相对于结构体起始地址的偏移量为0能被4整除对于变量b它的自身对齐参数为4#pragma pack(n)默认值为8则最终b的对齐参数为4接下来的地址相对于结构体的起始地址的偏移量为11不能够整除4所以需要在a后面填充3字节使得偏移量达到4然后再为b分配4字节的空间对于变量c它的自身对齐参数为2#pragma pack(n)默认值为8则最终c的对齐参数为2而接下来的地址相对于结构体的起始地址的偏移量为8能整除2所以直接为c分配2字节的空间。此时结构体所占的字节数为134210字节最后由于abc的最终对齐参数分别为142最大为4#pragma pack(n)的默认值为8则结构体变量最后的大小必须能被4整除。而10不能够整除4所以需要在后面填充2字节达到12字节。其存储如下|char|----|----|----| 4字节|--------int--------| 4字节|--short--|----|----| 4字节总共占12个字节对于node3含有静态数据成员typedef struct node3{int a;short b;static int c;}S3;则sizeof(S3)8.这里结构体中包含静态数据成员而静态数据成员的存放位置与结构体实例的存储地址无关(注意只有在C中结构体中才能含有静态数据成员而C中结构体中是不允许含有静态数据成员的)。其在内存中存储方式如下|--------int--------| 4字节|--short-|----|----| 4字节而变量c是单独存放在静态数据区的因此用siezof计算其大小时没有将c所占的空间计算进来。而对于node5里面含有结构体变量复制代码typedef struct node5{bool a;S1 s1;double b;int c;}S5;复制代码sizeof(S5)32。对于变量a其自身对齐参数为1#pragma pack(n)为8则a的最终对齐参数为1为它分配1字节的空间它相对于结构体起始地址的偏移量为0能被1整除对于s1它的自身对齐参数为4(对于结构体变量它的自身对齐参数为它里面各个变量最终对齐参数的最大值)#pragma pack(n)为8所以s1的最终对齐参数为4接下来的地址相对于结构体起始地址的偏移量为1不能被4整除所以需要在a后面填充3字节达到4为其分配8字节的空间对于变量b它的自身对齐参数为8#pragma pack(n)的默认值为8则b的最终对齐参数为8接下来的地址相对于结构体起始地址的偏移量为12不能被8整除所以需要在s1后面填充4字节达到16再为b分配8字节的空间对于变量c它的自身对齐参数为4#pragma pack(n)的默认值为8则c的最终对齐参数为4接下来相对于结构体其实地址的偏移量为24能够被4整除所以直接为c分配4字节的空间。此时结构体所占字节数为13848428字节。对于整个结构体来说各个变量的最终对齐参数为1484最大值为8#pragma pack(n)默认值为8所以最终结构体的大小必须是8的倍数因此需要在最后面填充4字节达到32字节。其存储如下|--------bool--------| 4字节|---------s1---------| 8字节|--------------------| 4字节|--------double------| 8字节|----int----|---------| 8字节另外可以显示地在程序中使用#pragma pack(n)来设置系统默认的对齐参数在显示设置之后则以设置的值作为标准其它的和上面所讲的类似就不再赘述了读者可以自行上机试验一下。如果需要取消设置可以用#pragma pack()来取消。作者海子出处http://www.cnblogs.com/dolphin0520/本博客中未标明转载的文章归作者海子和博客园共有欢迎转载但未经作者同意必须保留此段声明且在文章页面明显位置给出原文连接否则保留追究法律责任的权利。