关于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个字节对 ...
随机推荐
- CDHtmlDialog探索----Javascript与窗体交互
CDHtmlDialog提供了C++与网页的双向交互,通此一系统简单的宏调用可以把网页中各元素的事件直接映射到C++程序中,而在网页中调用C++功能代码就显的不那么直观了.归根结底交互的基理就是实现相 ...
- 【转】C++ 11 并发指南一(C++ 11 多线程初探)
引言 C++ 11自2011年发布以来已经快两年了,之前一直没怎么关注,直到最近几个月才看了一些C++ 11的新特性,算是记录一下自己学到的东西吧,和大家共勉. 相信Linux程序员都用过Pthrea ...
- codeforces 38G - Queue splay伸展树
题目 https://codeforces.com/problemset/problem/38/G 题意: 一些人按顺序进入队列,每个人有两个属性,地位$A$和能力$C$ 每个人进入时都在队尾,并最多 ...
- Java内置包装类
Java内置包装类有Object.Integer.Float.Double.Number.Charcter.Boolean.Byte.System. Number,是抽象类,也是超类(父类).Numb ...
- 前端 ----jQuery操作表单
05-使用jQuery操作input的value值 表单控件是我们的重中之重,因为一旦牵扯到数据交互,离不开form表单的使用,比如用户的注册登录功能等 那么通过上节知识点我们了解到,我们在使用j ...
- 如何将代码通过vs2017加载到GitHub
(1)登陆GitHub并注册账户,在用户中新建repository (2)建立后,会给出新建repository地址,将其复制 (3)用VS新建一个项目,勾选“新建Git存储库”或者打开一个已经创 ...
- Codeforces 1107G Vasya and Maximum Profit [单调栈]
洛谷 Codeforces 我竟然能在有生之年踩标算. 思路 首先考虑暴力:枚举左右端点直接计算. 考虑记录\(sum_x=\sum_{i=1}^x c_i\),设选\([l,r]\)时那个奇怪东西的 ...
- LuoGu P2835 刻录光盘
题目传送门 这个题和消息扩散那个题,一模一样啊 除了数据范围小一点,搜索能过之外,没啥区别 但是我写WA了QwQ不知道为什么 和消息扩散的代码fc/diff了半天也没找出来哪不一样 换了输入就过了反正 ...
- 安卓易学,爬坑不易—腾讯老司机的RecyclerView局部刷新爬坑之路
前言 安卓开发者都知道,RecyclerView比ListView要灵活的多,但不可否认的里面的坑也同样埋了不少人.下面让我们看看腾讯开发工程师用实例讲解自己踩坑时的解决方案和心路历程. 话说有图有真 ...
- Confluence 6 "Duplicate Key" 相关问题解决
如果你遇到了下面的错误信息,例如: could not insert: [bucket.user.propertyset.BucketPropertySetItem#bucket.user.prope ...
struct

{
char c1;
};