关于sizeof与#pragma pack 以及网络上关于字节对齐的一点感想
工作中面试中对于字节对齐基本上是必考一个知识点,而很多面试是网络上上原题。基本上背一背就可以写正确,而关于4字节对齐我相信很多人也只是一个基本地了解,对于一些题目就感觉有问题,而且很多blog后面仍然有很多人在问一些题目,说明该blog并没有讲清楚这个问题。然后我自己也有疑问,所以就找了一些更多资料,终于是解了。
比如说
对于32位机器
1 #pragma pack(4)
2 struct a{short a;short b;short c;} ;
3 sizeof(a)==6 //为什么不为8
不是说按4字节对齐那么最后面的一个short应该填充2字节的padding吗?该问题在百度知道上有人提问,问到最后我估计提问者也是花好久才可能明白,或者后面也没有明白
引用CSDN上一个讲解字节对齐评论中的提问:
大神,我发现有点小问题,可能是我理解不当
1 struct A{
2 char a;
3 double b;
4 int c;
5 char d;
6 };//sizeof(A) = 24
如果按照规矩来这个明显要按照8字节对齐,
可为什么32位计算机还是要按照8字节对齐?显然我按照4字节对齐,总线访问效率更高呀?我觉得为什么字节对齐不受制于总线长度?望不吝赐教,小生在此拜谢
该Blog地址:http://blog.csdn.net/21aspnet/article/details/6729724
总体来讲要解决上述问题应该了解不仅仅是4字节对齐这么简单地去记忆,应该按照
对于结构体,在使用sizeof的时候会进行字节的对齐,对齐的规则如下:
1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
备注:编译器在给结构体开辟空间时,首先找到结构体中最宽的基本数据类型,然后寻找内存地址能被该基本数据类型所整除的位置,作为结构体的首地址。
2) 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding);
备注:为结构体的一个成员开辟空间之前,编译器首先检查预开辟空间的首地址相对于结构体首地址的偏移是否是本成员的整数倍,若是,则存放本成员,反之,则在本成员和上一个成员之间填充一定的字节,以达到整数倍的要求,也就是将预开辟空间的首地址后移几个字节。
3)结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要,编译器会在最末一个成员之后加上填充字节(trailing padding)。
备注:结构体总大小是包括填充字节,最后一个成员满足上面两条以外,还必须满足第三条,否则就必须在最后填充几个字节以达到本条要求。
以上3条要认真学习就知道要对于结构体而讲要注意最宽的基本数据类型,还有每个成员对于首地址的offset是成员大小的整数倍就应该明白
struct
{
char a;
double b;
}A;//sizeof(A)=16
而不是单单记在常考的32位机下4字节对齐这么简单。
当然最后也推荐一个自己认为讲得很清楚地BLOG,我怕内容有删除,就转载过来。地址为http://blog.csdn.net/nellson/article/details/5293588
对于结构体,在使用sizeof的时候会进行字节的对齐,对齐的规则如下:
1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
备注:编译器在给结构体开辟空间时,首先找到结构体中最宽的基本数据类型,然后寻找内存地址能被该基本数据类型所整除的位置,作为结构体的首地址。
2) 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding);
备注:为结构体的一个成员开辟空间之前,编译器首先检查预开辟空间的首地址相对于结构体首地址的偏移是否是本成员的整数倍,若是,则存放本成员,反之,则在本成员和上一个成员之间填充一定的字节,以达到整数倍的要求,也就是将预开辟空间的首地址后移几个字节。
3)结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要,编译器会在最末一个成员之后加上填充字节(trailing padding)。
备注:结构体总大小是包括填充字节,最后一个成员满足上面两条以外,还必须满足第三条,否则就必须在最后填充几个字节以达到本条要求。
按照上面的规则,对如下几个例子进行分析:
例1:
1struct
DATA1
2{
3char c1;
//偏移量0,累积size = 1
4char c2;
//偏移量1,累积size = 1 + 1 = 2
5short si;
//偏移量2,累积size = 2 + 2
6};
这个例子中,首先找到结构体变量的首地址,按照规则1,该首地址需要能被最宽基本类型成员整除,即能被short(字节数2)整除。接下来,为c1分配内存,由于其占一个字节,此时内存状态为*,然后,为c2分配内存,由于结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,而c2是char型成员,占用一个字节,刚好能够对齐,此时内存状态为**,最后,为short型成员分配内存,由于前两个成员占2个字节,si相对于结构体首地址的偏移量刚好是2的整数倍,所以,此时内存状态为****
例2:
1struct
DATA2
2{
3char c1;
//偏移量0,累积size = 1
4short si;
//偏移量1 + (1),累积size = 1 + (1) + 2 = 4
5char c2;
//偏移量4,累积size = 4 + 1 = 5,但按最大长度sizeof(short) = 2对齐,故最后取6
6};
这个例子中,首先找到结构体变量的首地址,与上例同。接下来,为c1分配内存,由于其占一个字节,此时内存状态为*,然后,为si分配内存,由于结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,而si是short型成员,占用2个字节,而前一个成员占用一个字节,所以需要在c1与si之间补一个字节进行对齐,此时内存状态为*×**,最后,为char型成员分配内存,由于前两个成员占4个字节,c2相对于结构体首地址的偏移量是1的整数倍,所以,此时内存状态为*×***,接下来,按照规则3,结构体的总大小为结构体最宽基本类型成员大小的整数倍,所以总大小应该是short(2字节)型变量大小的整数倍,需要再补一个字节,即*×***×,总共占6个字节
例3:
1struct
DATA3
2{
3char c1;
//偏移量0,累积size = 1
4double d;
//偏移量1 + (7),累积size = 1 + (7) + 8 = 16
5char c2;
//偏移量16,累积size = 16 + 1 = 17,但按最大长度sizeof(double) = 8对齐,故最后取24
6};
关于#pragma pack(n)
VC中提供了#pragma pack(n)来设定变量以n字节对齐方式。n字节对齐就是说变量存放的起始地址的偏移量有两种情况:第一、如果n大于等于该变量所占用的字节数,那么偏移量必须满足默认的对齐方式,第二、如果n小于该变量的类型所占用的字节数,那么偏移量为n的倍数,不用满足默认的对齐方式。结构的总大小也有个约束条件,分下面两种情况:如果n大于所有成员变量类型所占用的字节数,那么结构的总大小必须为占用空间最大的变量占用的空间数的倍数;否则必须为n的倍数。
例4:
1#pragma
pack(push) //保存对齐状态
2#pragma
pack(2)//设定为2字节对齐
3struct
DATA4
4{
5char c1;
6double d1;
7int i1;
8short s1;
9};
10#pragma
pack(pop)//恢复对齐状态
以上结构的大小为16,下面分析其存储情况,首先为c1分配空间,其偏移量为0,c1占用1个字节,由于1<2,所以按照char类型变量默认的对其方式。接着开始为d分配空间,这时其偏移量为1,需要补足1个字节,这样使偏移量满足为n=2的倍数(因为sizeof(double)大于n),d占用8个字节。接着为i1分配空间,这时其偏移量为10,满足为2的倍数,i1占用4个字节。这时已经为所有成员变量分配了空间,共分配了14个字节。 接下来再为s1分配空间,由于min(sizeof(s1),2)=2,所以s1按照2字节对齐,此时已经是对其的,所以分配2字节内存,这时,总共占用16字节
例5:
1#pragma
pack(push) //保存对齐状态
2#pragma
pack(4)//设定为4字节对齐
3struct
DATA4
4{
5char c1;
6double d;
7int i1;
8short s1;
9};
10#pragma
pack(pop)//恢复对齐状态
以上结构的大小为20,下面分析其存储情况,首先为c1分配空间,其偏移量为0,c1占用1个字节,由于1<2,所以按照char类型变量默认的对其方式。接着开始为d分配空间,这时其偏移量为1,需要补足3个字节,这样使偏移量满足为n=4的倍数(因为sizeof(double)大于n),d占用8个字节。接着为i1分配空间,这时其偏移量为12,满足为4的倍数,i1占用4个字节。这时已经为所有成员变量分配了空间,共分配了16个字节。接下来再为s1分配空间,由于min(sizeof(s1),4)=2,所以s1按照2字节对齐,此时已经是对齐的,所以分配2字节内存,这时,总共占用16字节,由于占用内存总数需要为4的整数倍,所以结构大小为20
【参考文献】
1.《对几组sizeof信息的分析》 http://blog.vckbase.com/billdavid/archive/2004/06/23/509.html
2.《sizeof(结构体)和内存对齐》http://www.ksarea.com/articles/20071004_sizeof-struct-memory.html
关于sizeof与#pragma pack 以及网络上关于字节对齐的一点感想的更多相关文章
- stm32中使用#pragma pack(非常有用的字节对齐用法说明)
#pragma pack(4) //按4字节对齐,但实际上由于结构体中单个成员的最大占用字节数为2字节,因此实际还是按2字节对齐 typedef struct { char buf[3];//bu ...
- pragma pack(非常有用的字节对齐用法说明)
强调一点: #pragma pack(4) typedef struct { char buf[3]; word a; }kk; #pragma pack() 对齐的原则是min(sizeof(wor ...
- #Pragma Pack与内存分配
博客转载自:https://blog.csdn.net/mylinx/article/details/7007309 #pragma pack(n) 解释一: 每个特定平台上的编译器都有自己的默认“对 ...
- #pragma pack(push,1)与#pragma pack(1)的区别
这是给编译器用的参数设置,有关结构体字节对齐方式设置, #pragma pack是指定数据在内存中的对齐方式. #pragma pack (n) 作用:C编译器将按照n个字节对 ...
- C语言字节对齐 __align(),__attribute((aligned (n))),#pragma pack(n)
转载地址 : http://blog.csdn.net/21aspnet/article/details/6729724 一.概念 对齐跟数据在内存中的位置有关.如果一个变量的内存地址正好位于它 ...
- (转载)关于#pragma pack(push,1)和#pragma pack(1)
转载http://www.rosoo.net/a/201203/15889.html 一.#pragma pack(push,1)与#pragma pack(1)的区别 这是给编译器用的参数设置,有关 ...
- sizeof,终极无惑(上)
0. 前向声明 sizeof,一个其貌不扬的家伙,引无数菜鸟竟折腰,小虾我当初也没少犯迷糊,秉着“辛苦我一个,幸福千万人”的伟大思想,我决定将其尽可能具体的总结一下. 但当我总结的时候才发现,这个问题 ...
- #Pragma Pack(n)与内存分配
#pragma pack(n) 解释一: 每个特定平台上的编译器都有自己的默认"对齐系数"(也叫对齐模数).程序员可以通过预编译命令#pragma pack(n),n=1,2,4, ...
- 【转】#pragma pack(push,1)与#pragma pack(1)的区别
这是给编译器用的参数设置,有关结构体字节对齐方式设置, #pragma pack是指定数据在内存中的对齐方式. #pragma pack (n) 作用:C编译器将按照n个字节对 ...
随机推荐
- html5 - Storage 本地存储
Storage的解释 http://www.w3school.com.cn/html5/html_5_webstorage.asp 简单的理解就是: Storage 有两种: 1.localStora ...
- YUV的数据格式
一.YUV格式分为两大类:planar(平面)和packed(打包).planar格式,先连续存储所有像素点的Y分量,紧接着存储所有像素点的U,随后存储所有像素点的V.packed格式,每个像素点的Y ...
- 前端----css的继承性和层叠性
css有两大特性; 继承性和层叠性 继承性 继承:给父级设置一些属性,子级继承了父级的该属性, 这就是我们的css中的继承, 需要注意的是 有一些属性是可以继承下来的: color , font ...
- [MySQL]理解关系型数据库4个事务隔离级别
概述 SQL标准定义了4类隔离级别,包括了一些具体规则,用来限定事务内外的哪些改变是可见的,哪些是不可见的.低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销. 1. Read Uncommi ...
- [HTTP]Etag的工作流程
1. 浏览器首次访问该资源时,web服务器返回资源的同时,响应报文头携带ETag标签: 2. 浏览器将保存该Etag标签的值: 3. 当浏览器发起下一次请求,请求报文头将会携带 If-None-Mat ...
- windows下安装Rabbitmq详解
RabbitMQ是建立在强大的Erlang OTP平台上,因此安装Rabbit MQ的前提是安装Erlang. 1.什么是Erlang? Erlang(['ə:læŋ])是一种通用的面向并发的编程语言 ...
- Fiddler 抓包工具
序章 Fiddler是一个蛮好用的抓包工具,可以将网络传输发送与接受的数据包进行截获.重发.编辑.转存等操作.也可以用来检测网络安全.反正好处多多,举之不尽呀!当年学习的时候也蛮费劲,一些蛮实用隐藏的 ...
- ajax控件无法使用 iis配置及web修改(转载)
1.Web.config配置问题:将Web.config中的相关节配置成如下,然后重新编译你的程序:<httpHandlers><remove verb="*" ...
- Google开发者大会:你不得不知的Tensorflow小技巧
Google开发者大会:你不得不知的Tensorflow小技巧 同步滚动:开 Google Development Days China 2018近日在中国召开了.非常遗憾,小编因为不可抗性因素滞 ...
- 【MySql】删除操作
删除表内数据,用 delete.格式为: delete from 表名 where 删除条件; 实例:删除学生表内姓名为张三的记录. delete from student where T_name ...
struct

{
char c1;
};