结构体字节对齐
来源:互联网 发布:中国象棋电脑软件 编辑:程序博客网 时间:2024/06/14 07:03
结构体字节对齐
在用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 a,a的自身对齐参数为4(环境为windows/vc),而VC默认的对齐参数为8,取较小者,则对于a,它相对于结构体A的起始地址的偏移量必须是4的倍数。
对于第二条原则,结构体变量所占空间的大小是对齐参数的整数倍。这句话中的对齐参数有点复杂,它是取结构体中所有变量的对齐参数的最大值和系统默认对齐参数#pragma pack(n)比较,较小者作为对齐参数。举个例子假如在结构体A中先后定义了两个变量int a;double b;对于变量a,它的自身对齐参数为4,而#pragma pack(n)值默认为8,则a的对齐参数为4;b的自身对齐参数为8,而#pragma pack(n)的默认值为8,则b的对齐参数为8。由于a的最终对齐参数为4,b的最终对齐参数为8,那么两者较大者是8,然后再拿8和#pragma pack(n)作比较,取较小者作为对齐参数,也就是8,即意味着结构体最终的大小必须能被8整除。
下面是测试例子:
注意:以下例子的测试结果均在windows(32)/VC下测试的,其默认对齐参数为8
/*测试sizeof运算符 2011.10.1*/ #include <iostream>using 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[]){ cout<<sizeof(char)<<" "<<sizeof(short)<<" "<<sizeof(int)<<" "<<sizeof(float)<<" "<<sizeof(double)<<endl; S1 s1; S2 s2; S3 s3; S4 s4; S5 s5; cout<<sizeof(s1)<<" "<<sizeof(s2)<<" "<<sizeof(s3)<<" "<<sizeof(s4)<<" "<<sizeof(s5)<<endl; return 0;}
下面解释一下其中的几个结构体字节分配的情况
比如对于node2
typedef 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,接下来的地址相对于结构体的起始地址的偏移量为1,1不能够整除4,所以需要在a后面填充3字节使得偏移量达到4,然后再为b分配4字节的空间;
对于变量c,它的自身对齐参数为2,#pragma pack(n)默认值为8,则最终c的对齐参数为2,而接下来的地址相对于结构体的起始地址的偏移量为8,能整除2,所以直接为c分配2字节的空间。
此时结构体所占的字节数为1+3+4+2=10字节
最后由于a,b,c的最终对齐参数分别为1,4,2,最大为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字节的空间。
此时结构体所占字节数为1+3+8+4+8+4=28字节。
对于整个结构体来说,各个变量的最终对齐参数为1,4,8,4,最大值为8,#pragma pack(n)默认值为8,所以最终结构体的大小必须是8的倍数,因此需要在最后面填充4字节达到32字节。其存储如下:
|--------bool--------| 4字节
|---------s1---------| 8字节
|--------------------| 4字节
|--------double------| 8字节
|----int----|---------| 8字节
另外可以显示地在程序中使用#pragma pack(n)来设置系统默认的对齐参数,在显示设置之后,则以设置的值作为标准,其它的和上面所讲的类似,就不再赘述了,读者可以自行上机试验一下。如果需要取消设置,可以用#pragma pack()来取消。
![](http://static.cnblogs.com/images/icon_weibo_24.png)
» 下一篇:关于数组指针的一道面试题
内存分配应该是:
|---------bool--------| 8字节
|----------s1---------| 8字节
|---------double------| 8字节
|-------int--|--------| 8字节
s1的对其方式不是按他的里面的最大数据成员最大字节数啊?
s1本身是一个结构体成员变量,把它放到S5中的话,存储时是以整体为单位进行存储。对于结构体S1来说,一个它的变量占8字节,把这个变量放到另外一个结构体中,它仍然占8字节。
假如说你s1的大小是10个字节呢?那该怎么处理呢?
![](http://pic002.cnblogs.com/images/2012/316779/2012051416390988.jpg)
恩,我再仔细研究一下,稍后再给您回复。
好的,谢谢!
之前是我的理解错误,已经纠正。
![](http://images.cnitblog.com/blog/510750/201404/192150245418922.png)
这个帮忙看看
4+4+8(1+7个补充字节)=16吗 为什么是12呢
不是的,根据此文讲解,在win(32)vc下面,如果使用默认对其参数的话,即不设置#pragma pack(n),此时结构体的对齐参数就是结构体中最大元素的字节数(结构体嵌套除外),结构体的大小就是其对齐参数(最大元素字节数)的整数倍,这种情况下你的说法是对的,但如果是如下情况,这种说法就不对了,比如
#pragma pack(4)
struct node {
int i;
double d;
};
对齐参数是4,所以存储形式为
|-------i-------| 4字节
|------d-------| 4字节
|------d-------| 4字节
大小为12字节
#pragma pack(8)
struct example {
int a;
int b;
char c;
};
结构体最大元素字节数是4字节,pragma指定对齐参数为8,两者取最小的,最终确定对齐参数为4,所以存储形式为
|-------a-------| 4字节
|-------b-------| 4字节
|--c--|---------| 4字节
所以是12字节
- 结构体字节对齐
- 结构体字节对齐
- 结构体字节对齐
- 结构体字节对齐
- 结构体字节对齐
- 结构体字节对齐
- 结构体字节对齐
- 结构体字节对齐
- 结构体字节对齐
- 结构体字节对齐
- 结构体字节对齐
- 结构体字节对齐
- 结构体字节对齐
- 结构体字节对齐
- 结构体字节对齐
- 结构体字节对齐
- 结构体字节对齐
- 结构体字节对齐
- Unity3d NGUI的使用(七)(UIPopup List&使用UIPopup本地化项目)
- List of Sbi Ifsc Code in Kolkata
- Javascript中console.log()用法
- 结构体对齐
- leetcode - Generate Parentheses
- 结构体字节对齐
- POJ 1252 Euro Efficiency(BFS or 完全背包)
- INI文件读写
- 哈哈
- HDU 4791 Alice's Print Service(二分)
- JavaMail使用之属性的构建
- 分布式文件系统介绍
- 世界向何处去,和平发展还有没有可能?中国如何抓住第二个重要的战略机遇期?
- Spring AOP的实现之建立AopProxy代理对象
|--------bool--------| 4字节
|---------s1---------| 4字节
|---------s1---------| 4字节
|---------------------| 空出
|--------double-----| 8字节
|----int----|---------| 8字节
总共32个B