工作中面试中对于字节对齐基本上是必考一个知识点,而很多面试是网络上上原题。基本上背一背就可以写正确,而关于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吗?该问题在百度知道上有人提问,问到最后我估计提问者也是花好久才可能明白,或者后面也没有明白

百度提问地址:http://zhidao.baidu.com/link?url=SVyAiSChbzcvTNIWuQP8RbfleHxTYJPgtSbGtpm5dOQM3M7FHZzqy0UVmqL5zNOuO2XeELgGbg7cafRuIs5_Za

引用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

3    char    c1;
//偏移量0,累积size = 1 

4    char    c2;
//偏移量1,累积size = 1 + 1 = 2 

5    short    si;
//偏移量2,累积size = 2 + 2 

6}; 



这个例子中,首先找到结构体变量的首地址,按照规则1,该首地址需要能被最宽基本类型成员整除,即能被short(字节数2)整除。接下来,为c1分配内存,由于其占一个字节,此时内存状态为*,然后,为c2分配内存,由于结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,而c2是char型成员,占用一个字节,刚好能够对齐,此时内存状态为**,最后,为short型成员分配内存,由于前两个成员占2个字节,si相对于结构体首地址的偏移量刚好是2的整数倍,所以,此时内存状态为**** 



例2: 

1struct
DATA2 

2

3    char    c1;
//偏移量0,累积size = 1 

4    short    si;
//偏移量1 + (1),累积size = 1 + (1) + 2 = 4 

5    char    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

3    char    c1;
//偏移量0,累积size = 1 

4    double    d;
//偏移量1 + (7),累积size = 1 + (7) + 8 = 16 

5    char    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

5    char    c1; 

6    double  d1; 

7    int    i1; 

8    short  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

5    char    c1; 

6    double  d; 

7    int    i1; 

8    short  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 以及网络上关于字节对齐的一点感想的更多相关文章

  1. stm32中使用#pragma pack(非常有用的字节对齐用法说明)

    #pragma pack(4)   //按4字节对齐,但实际上由于结构体中单个成员的最大占用字节数为2字节,因此实际还是按2字节对齐 typedef struct { char buf[3];//bu ...

  2. pragma pack(非常有用的字节对齐用法说明)

    强调一点: #pragma pack(4) typedef struct { char buf[3]; word a; }kk; #pragma pack() 对齐的原则是min(sizeof(wor ...

  3. #Pragma Pack与内存分配

    博客转载自:https://blog.csdn.net/mylinx/article/details/7007309 #pragma pack(n) 解释一: 每个特定平台上的编译器都有自己的默认“对 ...

  4. #pragma pack(push,1)与#pragma pack(1)的区别

    这是给编译器用的参数设置,有关结构体字节对齐方式设置, #pragma pack是指定数据在内存中的对齐方式. #pragma pack (n)             作用:C编译器将按照n个字节对 ...

  5. C语言字节对齐 __align(),__attribute((aligned (n))),#pragma pack(n)

    转载地址 : http://blog.csdn.net/21aspnet/article/details/6729724 一.概念    对齐跟数据在内存中的位置有关.如果一个变量的内存地址正好位于它 ...

  6. (转载)关于#pragma pack(push,1)和#pragma pack(1)

    转载http://www.rosoo.net/a/201203/15889.html 一.#pragma pack(push,1)与#pragma pack(1)的区别 这是给编译器用的参数设置,有关 ...

  7. sizeof,终极无惑(上)

    0. 前向声明 sizeof,一个其貌不扬的家伙,引无数菜鸟竟折腰,小虾我当初也没少犯迷糊,秉着“辛苦我一个,幸福千万人”的伟大思想,我决定将其尽可能具体的总结一下. 但当我总结的时候才发现,这个问题 ...

  8. #Pragma Pack(n)与内存分配

    #pragma pack(n) 解释一: 每个特定平台上的编译器都有自己的默认"对齐系数"(也叫对齐模数).程序员可以通过预编译命令#pragma pack(n),n=1,2,4, ...

  9. 【转】#pragma pack(push,1)与#pragma pack(1)的区别

    这是给编译器用的参数设置,有关结构体字节对齐方式设置, #pragma pack是指定数据在内存中的对齐方式. #pragma pack (n)             作用:C编译器将按照n个字节对 ...

随机推荐

  1. hibernate框架学习之数据查询(HQL)helloworld

    package cn.itcast.h3.hql; import java.util.List; import org.hibernate.Query; import org.hibernate.Se ...

  2. Apollo 框架的剖析1

    百度Apollo 自动驾驶开源模块分析 从今天开始研究学习apollo的源码,apollo 3.0源码. apollo 3.0的系统框图 文件目录简介 apollo根目录 ├── .github/IS ...

  3. 使用vue-cli初始化vue项目

    在项目中使用vue我使用vue-cli脚手架搭建项目 1.先安装nodejs 2.使用npm install -g vue-cli (建议在使用这步前先安装nrm来切换npm的源利器,使得下载资源更快 ...

  4. python操作三大主流数据库(1)python操作mysql①windows环境中安装python操作mysql数据库的MySQLdb模块mysql-client

    windows安装python操作mysql数据库的MySQLdb模块mysql-client 正常情况下应该是cmd下直接运行 pip install mysql-client 命令即可,试了很多台 ...

  5. linux备忘簿

    1.ubuntu中按ctrl+s锁定屏幕,按ctrl+q解锁. 2.vim中撤销和恢复为u和ctlr+r 3.静态库和动态库编译命令: (1)得到hello.o g++ -c hello.cpp (2 ...

  6. 前端 -----02 body标签中相关标签

    今日内容: 字体标签: h1~h6.<font>.<u>.<b>.<strong><em>.<sup>.<sub> ...

  7. IntelliJ IDEA使用教程 (总目录篇)

    注:本文来源于<    IntelliJ IDEA使用教程 (总目录篇)  > IntelliJ IDEA使用教程 (总目录篇) 硬件要求 IntelliJ IDEA 的硬件要求 安装包云 ...

  8. Linux Oracle bash: “sqlplus / as sysdba”: command not found 解决方法

    bash: sqlplus: command not found 解决方法 注:本文来源于 <   bash: sqlplus: command not found 解决方法   > 1: ...

  9. Confluence 6 连接到 Jira 用户管理的建议

    建议 如果下面所有的选项都为是的话: JIRA 应用程序不在高负载下运行. 你仅仅希望在一些不多的应用中跨平台管理你的用户和用户组,例如一个 JIRA 软件服务器和 Confluence 服务器,或者 ...

  10. Flask框架(一)

    Flask框架 Flask本身想当于一个内核,其自身几乎所有功能都依靠扩展(邮件扩展Flask-Mail.用户认证Flask-Login),都需要用第三方的扩展来实现.其WSGI工具箱采用Werkze ...