结构体字节对齐

一、为什么结构体内存对齐

其实我们都知道,结构体只是一些数据的集合,它本身什么都没有。我们所谓的结构体地址,其实就是结构体第一个元素的地址。这样,如果结构体各个元素之间不存在内存对齐问题,他们都挨着排放的。对于32位机,32位编译器(这是目前常见的环境,其他环境也会有内存对齐问题),就很可能操作一个问题,就是当你想要去访问结构体中的一个数据的时候,需要你操作两次数据总线,因为这个数据卡在中间,如图:

在上图中,对于第2个short数据进行访问的时候,在32位机器上就要操作两次数据总线。这样会非常影响数据读写的效率,所以就引入了内存对齐的问题。另外一层原因是:某些硬件平台只能从规定的地址处取某些特定类型的数据,否则会抛出硬件异常。

二、结构体内存对齐的规则

下表是Windows XP/DEV-C++和Linux/GCC中基本数据类型的长度和默认对齐模数。

char short int long float double long long long double
Win-32 长度 1 2 4 4 4 8 8 8
Linux-32 长度 1 2 4 4 4 8 8 12
Linux-64 长度 1 2 4 8 4 8 8 16
MAC-64 长度 1 2 4 8 4 8 8 16

1.未指定#pragma pack时

a.第一个成员起始于0偏移处;
b.每个成员按其类型大小和指定对齐参数n中较小的一个进行对齐;
c.结构体总长度必须为所有对齐参数的整数倍,或者理解为最大类型的整数倍;
d.对于数组,可以拆开看做n个数组元素。

2.指定#pragma pack(n)时

a. n必须为0 1 2 4 8 16,其中为0时就相当于未指定#pragma pack
b. 当结构体中的类型小于n时,按该类型的对齐规则对齐,对于大于等于n的类型,按n对齐。
c.结构体总长度: 1.当结构体中的类型全都小于n时,总长度为所有对齐参数的整数倍,或者理解为最大类型的整数倍;2.当结构体中有大于等于n的类型,按n的整数倍对齐。

三、具体举例 运行环境为MAC-64

1.未指定#pragma pack时

例1
struct S1
{

    short a1;
    short a2;
    short a3;

};
struct S2
{
    long a1;
    short a2;
};

打印结果为 size of s1:6 -- size of s2:16
sizeof(S1) = 6; 这个很好理解,三个short都为2。

例2
struct S1{
  int a;
  char b;
  short c;
};

struct S2{
  char b;
  int a;
  short c;
};

打印结果:size of s1:8 -- size of s2:12


字节对齐示意图

上图中一个单元格表示一个字节 1表示占用,0表示空闲
由上图可以看出要按类型字节长度的整数倍位置对齐,其他位置被空闲。最终所占长度为最大类型的整数倍。

  1. S1中a 占4个字节。b占1个字节,c占2个字节,但是c要从2的倍数处对齐,因此,正好S1占8个字节,是所有类型的整数倍,占8个字节。
  2. S2中b占1个字节 占0位置,a 占4个字节 从索引4开始对齐,c占2个字节 从索引8开始对齐,总长为10,但是整个空间应该为所有类型的整数倍,因此需要2个空闲位。总共12个字节
例3 下面是结构体嵌套情况
struct S1{
     int a;
     double b;
     float c;
};

struct S2{
     char e[2];
     int f;
     double g;
     short h;
     struct S1 s;
};

打印结果:size of s1:24 -- size of s2:48
sizeof(S1) = 24; 这个比较好理解,int为4,double为8,float为4,总长为8的倍数,补齐,所以整个S1为24。
我们看看S2的内存布局:

e f g h s
1 1 * * 1 1 1 1 1 1 1 1 1 1 1 1 1 1 * * * * * * 1111**** 11111111 11******

s 为结构体S1 看做一个整体,他的最大类型double为8位 需要按8的倍数对齐,因此从索引24开始 因此总共48位

例4
struct S1
{
  short a;
  int b;
};

struct S2
{
    char c;
    struct S1 d;
    double e;
};

打印结果:size of s1:8 -- size of s2:24
内存布局如下:

S1 a b
1 1 * * 1 1 1 1
S2 c d e
1 * * * 11** 1111 * * * * 1 1 1 1 1 1 1 1

综上可知

在结构体嵌套的情况下 结构体看做一个整体,他按他自己的最大成员的类型进行对齐,最终按整个结构体中最大类型对齐。

指定#pragma pack(n)时(#pragma pack 是编译预处理指令,可以指定按多少字节对齐)

a. n必须为0 1 2 4 8 16,其中为0时就相当于未指定#pragma pack
b. 当结构体中的类型小于n时,按该类型的对齐规则对齐,对于大于等于n的类型,按n对齐。
c.结构体总长度: 1.当结构体中的类型全都小于n时,总长度为所有对齐参数的整数倍,或者理解为最大类型的整数倍;
2.当结构体中有大于等于n的类型,按n的整数倍对齐。

例1
#pragma pack(1)
struct S1
{
    char  a;
    short b;
    short c;

};
struct S2
{
    long d;
    short e;
    char f;
};

打印结果为:size of s1:5 -- size of s2:11
很好理解n=1时,按顺序排放

pragma pack(2) 时的结果:size of s1:6 -- size of s2:12
此时 S1中的short类型正好等于2个字节 因此按2个字节对齐,S2中long类型占8字节大于2 因此按2字节对齐

pragma pack(4) 时的结果: size of s1:6 -- size of s2:12
此时S1中的所有类型长度都小于4,因此按S1中最大类型short对齐。S2中long类型占8字节大于4 因此按4字节对齐

pragma pack(8) 时的结果:size of s1:6 -- size of s2:16
此时S1中的所有类型长度都小于8,因此按S1中最大类型short对齐。
S2中long类型占8字节正好等于8 因此按8字节对齐,因此为16字节

pragma pack(16) 时的结果:size of s1:6 -- size of s2:16
此时S1还是所有类型小于16,因此按S1中最大类型short对齐。
S2中long类型占8字节小于16 因此按S2中最大类型long 8字节对齐,因此为16字节.

例2 结构体嵌套情况
#pragma pack(1)
struct S1
{
  short a;
  int b;
};

struct S2
{
    char c;
    struct S1 d;
    double e;
};

打印结果:size of s1:6 -- size of s2:15
很好理解所有结构体顺序排放

pragma pack(2) 的时候:size of s1:6 -- size of s2:16
S1还是6字节。
S2的布局情况如下

S2 c d e
1 * 11 1111 11111111

pragma pack(4) 的时候: size of s1:8 -- size of s2:20
此时S1中的int类型正好是4 因此按4字节对齐 因此占8字节
S2的内存布局如下:

S2 c d e
1 *** 11**1111 11111111

此时因为d是S1类型 S1看做一个整体,在S1中按4字节对齐 因此从4开始布局,e由于大于pack(4) 因此按4字节对齐排序,从12索引出开始布局。因此总共20字节 也是4的整数倍。

pragma pack(8) 的时候: size of s1:8 -- size of s2:24
此时S1中的int类型小于8,因此按S1中最大类型int对齐
S2的内存布局如下:

S2 c d e
1 *** 11**1111 **** 11111111

此时S2中最大类型(double)的成员e正好等于8,因此按8字节对齐,因此从索引16出开始布局,最终为24个字节

pragma pack(16) 的时候: size of s1:8 -- size of s2:24
此时S2中最大类型(double)成员e小于16,因此按8字节对齐,因此情况和pragma pack(8) 的时候一样。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,657评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,662评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,143评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,732评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,837评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,036评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,126评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,868评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,315评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,641评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,773评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,859评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,584评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,676评论 2 351

推荐阅读更多精彩内容

  • 结构体 字节对齐的练习 分析结构体大小struct S1 ...
    Asura_Luo阅读 712评论 0 1
  • @[c++|struct] 今天在编程中碰到一个坑,搞的调试了半天,最后发现程序中在写数据和读取数据时结构体定义不...
    drybeans阅读 3,590评论 1 11
  • 虽然内存是以自己为单位的,但大部分计算机CPU在处理内存时,并不会直接以字节为单位。处理为了提高读取速度和减少出错...
    Niuszeng阅读 581评论 0 1
  • 在用sizeof运算符求算某结构体所占空间时,并不是简单地将结构体中所有元素各自占的空间相加,这里涉及到内存字节对...
    无涯之涯阅读 588评论 0 0
  • 在用sizeof运算符求算某结构体所占空间时,并不是简单地将结构体中所有元素各自占的空间相加,这里涉及到内存字节对...
    saygoodbye_e92e阅读 881评论 0 0