精华内容
下载资源
问答
  • 结构体字节对齐

    2021-05-17 23:52:53
    在用sizeof运算符求算某结构体所占空间时,并不是简单地将结构体中所有元素各自占的空间相加,这里涉及到内存字节对齐的问题。从理论上讲,对于任何 变量的访问都可以从任何地址开始访问,但是事实上不是如此,实际...

    在用sizeof运算符求算某结构体所占空间时,并不是简单地将结构体中所有元素各自占的空间相加,这里涉及到内存字节对齐的问题。从理论上讲,对于任何 变量的访问都可以从任何地址开始访问,但是事实上不是如此,实际上访问特定类型的变量只能在特定的地址访问,这就需要各个变量在空间上按一定的规则排列, 而不是简单地顺序排列,这就是内存对齐。

    内存对齐的原因:

    1)某些平台只能在特定的地址处访问特定类型的数据;

    2)提高存取数据的速度。比如有的平台每次都是从偶地址处读取数据,对于一个int型的变量,若从偶地址单元处存放,则只需一个读取周期即可读取该变量;但是若从奇地址单元处存放,则需要2个读取周期读取该变量。

    win32平台下的微软C编译器对齐策略:

    1)结构体变量的首地址能够被其最宽数据类型成员的大小整除。编译器在为结构体变量开辟空间时,首先找到结构体中最宽的数据类型,然后寻找内存地址能被该数据类型大小整除的位置,这个位置作为结构体变量的首地址。而将最宽数据类型的大小作为对齐标准。

    2)结构体每个成员相对结构体首地址的偏移量(offset)都是每个成员本身大小的整数倍,如有需要会在成员之间填充字节。编译器在为结构体成员开辟空 间时,首先检查预开辟空间的地址相对于结构体首地址的偏移量是否为该成员大小的整数倍,若是,则存放该成员;若不是,则填充若干字节,以达到整数倍的要 求。

    3)结构体变量所占空间的大小必定是最宽数据类型大小的整数倍。如有需要会在最后一个成员末尾填充若干字节使得所占空间大小是最宽数据类型大小的整数倍。

    下面看一下sizeof在计算结构体大小的时候具体是怎样计算的

    1.test1   空结构体

    typedef struct node

    {

    }S;

    则sizeof(S)=1;或sizeof(S)=0;

    在C++中占1字节,而在C中占0字节。

    2.test2

    typedef struct node1

    {

    int a;

    char b;

    short c;

    }S1;

    则sizeof(S1)=8。这是因为结构体node1中最长的数据类型是int,占4个字节,因此以4字节对齐,则该结构体在内存中存放方式为

    |--------int--------|   4字节

    |char|----|--short-|   4字节

    总共占8字节

    3.test3

    typedef struct node2

    {

    char a;

    int b;

    short c;

    }S2;

    则siezof(S3)=12.最长数据类型为int,占4个字节。因此以4字节对齐,其在内存空间存放方式如下:

    |char|----|----|----|  4字节

    |--------int--------|  4字节

    |--short--|----|----|  4字节

    总共占12个字节

    4.test4  含有静态数据成员

    typedef struct node3

    {

    int a;

    short b;

    static int c;

    }S3;

    则sizeof(S3)=8.这里结构体中包含静态数据成员,而静态数据成员的存放位置与结构体实例的存储地址无关(注意只有在C++中结构体中才能含有静态数据成员,而C中结构体中是不允许含有静态数据成员的)。其在内存中存储方式如下:

    |--------int--------|   4字节

    |--short-|----|----|    4字节

    而变量c是单独存放在静态数据区的,因此用siezof计算其大小时没有将c所占的空间计算进来。

    5.test5  结构体中含有结构体

    typedef struct node4

    {

    bool a;

    S1 s1;

    short b;

    }S4;

    则sizeof(S4)=16。是因为s1占8字节,而s1中最长数据类型为int,占4个字节,bool类型1个字节,short占2字节,因此以4字节对齐,则存储方式为

    |-------bool--------|  4字节

    |-------s1----------|  8字节

    |-------short-------|  4字节

    6.test6

    typedef struct node5

    {

    bool a;

    S1 s1;

    double b;

    int c;

    }S5;

    则sizeof(S5)=32。是因为s1占8字节,而s1中最长数据类型为int,占4字节,而double占8字节,因此以8字节对齐,则存放方式为:

    |--------bool--------|    8字节

    |---------s1---------|    8字节

    |--------double------|    8字节

    |----int----|---------|     8字节

    7.test7

    若在程序中使用了#pragma pack(n)命令强制以n字节对齐时,默认情况下n为8.

    则比较n和结构体中最长数据类型所占的字节大小,取两者中小的一个作为对齐标准。

    若需取消强制对齐方式,则可用命令#pragma pack()

    如果在程序开头使用命令#pragma pack(4),对于下面的结构体

    typedef struct node5

    {

    bool a;

    S1 s1;

    double b;

    int c;

    }S5;

    则sizeof(S5)=24.因为强制以4字节对齐,而S5中最长数据类型为double,占8字节,因此以4字节对齐。在内存中存放方式为:

    |-----------a--------|   4字节

    |--------s1----------|   4字节

    |--------s1----------|   4字节

    |--------b-----------|   4字节

    |--------b-----------|   4字节

    |---------c----------|    4字节

    总结一下,在计算sizeof时主要注意一下几点:

    1)若为空结构体,则只占1个字节的单元

    2)若结构体中所有数据类型都相同,则其所占空间为 成员数据类型长度×成员个数

    若结构体中数据类型不同,则取最长数据类型成员所占的空间为对齐标准,数据成员包含另一个结构体变量t的话,则取t中最 长数据类型与其他数据成员比较,取最长的作为对齐标准,但是t存放时看做一个单位存放,只需看其他成员即可。

    3)若使用了#pragma pack(n)命令强制对齐标准,则取n和结构体中最长数据类型占的字节数两者之中的小者作为对齐标准。

    另外除了结构体中存在对齐之外,普通的变量存储也存在字节对齐的情况,即自身对齐。编译器规定:普通变量的存储首地址必须能被该变量的数据类型宽度整除。

    测试程序:

    展开全文
  • 如上结构体变量 data 占多少字节?char 占 1 字节,int 占 4 字节,所以总共占 5 字节吗?我们写一个程序验证一下: # include <stdio.h> struct STUDENT { char a; int b; }data; int main(void) { ...

    问大家一个问题:

    struct STUDENT
    {
        char a;
        int b;
    }data;

    如上结构体变量 data 占多少字节?char 占 1 字节,int 占 4 字节,所以总共占 5 字节吗?我们写一个程序验证一下:

    # include <stdio.h>
    struct STUDENT
    {
        char a;
        int b;
    }data;
    int main(void)
    {
        printf("%p, %p\n", &data.a, &data.b);  //%p是取地址输出控制符
        printf("%d\n", sizeof(data));
        return 0;
    }

    输出结果是:
    00427E68, 00427E6C
    8

    我们看到 data 不是占 5 字节,而是占 8 字节。变量 a 的地址是从 00427E68 到 00427E6B,占 4字 节;变量 b 的地址是从 00427E6C 到 00427E6F,也占 4 字节。b 占 4 字节我们能理解,但 a 是 char 型,char 型不是占 1 字节吗,这里为什么占 4 字节?其实不是它占了 4 字节,它占的还是 1 字节,只不过结构体中有一个字节对齐的概念。

    什么叫字节对齐?我们知道结构体是一种构造数据类型,里面可以有不同数据类型的成员。在这些成员中,不同的数据类型所占的内存空间是不同的。那么系统是怎么给结构体变量的成员分配内存的呢?或者说这些成员在内存中是如何存储的呢?通过上面这个例子我们知道肯定不是顺序存储的。

    那么到底是怎么存储的呢?就是按字节对齐的方式存储的!即以结构体成员中占内存最多的数据类型所占的字节数为标准,所有的成员在分配内存时都要与这个长度对齐。我们举一个例子:我们以上面这个程序为例,结构体变量 data 的成员中占内存最多的数据类型是 int 型,其占 4 字节的内存空间,那么所有成员在分配内存时都要与 4 字节的长度对齐。也就是说,虽然 char 只占 1 字节,但是为了与 4 字节的长度对齐,它后面的 3 字节都会空着,即:
     

    a
    b


    所谓空着其实也不是里面真的什么都没有,它就同定义了一个变量但没有初始化一样,里面是一个很小的、负的填充字。为了便于表达,我们就暂且称之为空好了。

    如果结构体成员为:

    struct STUDENT
    {
        char a;
        char b;
        int c;
    }data;

    那么这三个成员是怎么对齐的?a 和 b 后面都是空 3 字节吗?不是!如果没有 b,那么 a 后面就空 3 字节,有了 b 则 b 就接着 a 后面填充。即:
     

    ab
    c


    所以这时候结构体变量 data 仍占 8 字节。我们写一个程序验证一下:

    # include <stdio.h>
    struct STUDENT
    {
        char a;
        char b;
        int c;
    }data;
    int main(void)
    {
        printf("%p, %p, %p\n", &data.a, &data.b, &data.c);  //%p是取地址输出控制符
        printf("%d\n", sizeof(data));
        return 0;
    }

    输出结果是:
    00427E68, 00427E69, 00427E6C
    8

    这时我们发现一个问题:所有成员在分配内存的时候都与 4 字节的长度对齐,多个 char 类型时是依次往后填充,但是 char 型后面的 int 型为什么不紧接着后面填充?为什么要另起一行?也就是说,到底什么时候是接在后面填充,什么时候是另起一行填充?

    我们说,所有的成员在分配内存时都要与所有成员中占内存最多的数据类型所占内存空间的字节数对齐。假如这个字节数为 N,那么对齐的原则是:理论上所有成员在分配内存时都是紧接在前一个变量后面依次填充的,但是如果是“以 N 对齐”为原则,那么,如果一行中剩下的空间不足以填充某成员变量,即剩下的空间小于某成员变量的数据类型所占的字节数,则该成员变量在分配内存时另起一行分配。

    下面再来举一个例子,大家觉得下面这个结构体变量data占多少字节?

    struct STUDENT
    {
        char a;
        char b;
        char c;
        char d;
        char e;
        int f;
    }data;

    首先最长的数据类型占 4 字节,所以是以 4 对齐。然后 a 占 1 字节,b 接在 a 后面占 1 字节,c 接在 b 后面占 1 字节,d 接在 c 后面占 1 字节,此时满 4 字节了,e 再来就要另起一行。f 想紧接着 e 后面分配,但 e 后面还剩 3 字节,小于 int 类型的 4 字节,所以 f 另起一行。即该结构体变量分配内存时如下:
     

    abcd
    e
    f


    即总共占 12 字节。我们写一个程序验证一下:

    # include <stdio.h>
    struct STUDENT
    {
        char a;
        char b;
        char c;
        char d;
        char e;
        int f;
    }data;
    int main(void)
    {
        printf("%p, %p, %p, %p, %p, %p\n", &data.a, &data.b, &data.c, &data.d, &data.e, &data.f);  //%p是取地址输出控制符
        printf("%d\n", sizeof(data));
        return 0;
    }

    输出结果是:
    00427E68, 00427E69, 00427E6A, 00427E6B, 00427E6C, 00427E70
    12

    现在大家应该能掌握字节对齐的精髓了吧!下面给大家出一个题目试试掌握情况。我们将前面的结构体改一下:

    struct STUDENT
    {
        char a;
        int b;
        char c;
    }data;

    即将原来第二个和第三个声明交换了位置,大家看看现在 data 变量占多少字节?没错,是 12 字节。首先最长类型所占字节数为 4,所以是以 4 对齐。分配内存的时候 a 占 1 字节,然后 b 想紧接着 a 后面存储,但 a 后面还剩 3 字节,小于 b 的 4 字节,所以 b 另起一行分配。然后 c 想紧接着 b 后面分配,但是 b 后面没空了,所以 c 另起一行分配。所以总共 12 字节。内存分配图如下所示:
     

    a
    b
    c


    下面写一个程序验证一下:

    # include <stdio.h>
    struct STUDENT
    {
        char a;
        int b;
        char c;
    }data;
    int main(void)
    {
        printf("%p, %p, %p\n", &data.a, &data.b, &data.c);  //%p是取地址输出控制符
        printf("%d\n", sizeof(data));
        return 0;
    }

    输出结果是:
    00427E68, 00427E6C, 00427E70
    12

    我们看到,同样三个数据类型,只不过交换了一下位置,结构体变量data所占的内存空间就由8字节变成12字节,多了4字节。这就告诉我们,在声明结构体类型时,各类型成员的前后位置会对该结构体类型定义的结构体变量所占的字节数产生影响。没有规律的定义会增加系统给结构体变量分配的字节数,降低内存分配的效率。但这种影响对操作系统来说几乎是可以忽略不计的!所以我们在写程序的时候,如果有心的话,声明结构体类型时就按成员类型所占字节数从小到大写,或从大到小写。但是如果没有按规律书写的话也不要紧,声明结构体类型时并非一定要从小到大声明,只是为了说明“字节对齐”这个概念!而且有时候为了增强程序的可读性我们就需要没有规律地写,比如存储一个人的信息:

    struct STUDENT
    {
        char name[10];
        int age;
        char sex;
        float score;
    }data;

    正常的思维是将“性别”放在“年龄”后面,但如果为了内存对齐而交换它们的位置,总让人觉得有点别扭。所以我说“尽量”有规律地写!

    这时又有人会提出一个问题:“上面这个结构体变量 data 中有成员 char name[10],长度最长,是 10,那是不是要以 10 对齐?”不是,char a[10] 的本质是 10 个 char 变量,所以就把它当成 10 个 char 变量看就行了。所以结构体变量 data 中成员最长类型占 4 字节,还是以 4 对齐。该结构体变量分配内存时情况如下:
     

    name[0]name[1]name[2]name[3]
    name[4]name[5]name[6]name[7]
    name[8]name[9]
    age
    sex
    float


    总共 24 字节,我们写一个程序验证一下:

    # include <stdio.h>
    struct STUDENT
    {
        char name[10];
        int age;
        char sex;
        float score;
    }data;
    int main(void)
    {
        printf("%p, %p, %p, %p, %p, %p, %p, %p, %p, %p, %p, %p, %p\n", &data.name[0], &data.name[1], &data.name[2], &data.name[3], &data.name[4], &data.name[5], &data.name[6], &data.name[7], &data.name[8], &data.name[9], &data.age, &data.sex, &data.score);
        printf("%d\n", sizeof(data));
        return 0;
    }

    输出结果是:
    00427E68, 00427E69, 00427E6A, 00427E6B, 00427E6C, 00427E6D, 00427E6E,
    00427E6F, 00427E70, 00427E71, 00427E74, 00427E78, 00427E7C
    24

    展开全文
  • C++结构体字节对齐

    2021-05-21 19:14:50
    计算机系统对基本类型数据在内存中存放的位置有限制,要求这些数据的起始地址的值是某个数k的倍数,这就是所谓的内存对齐,而这个k则被称为该数据类型的对齐模数(alignment modulus)。结构的存储分配编译器按照...

    前言

    在计算机中数据存储和传输以位(bit)为单位,每8个位bit组成1个字节(Byte)。32位计算机的字长为32位,即4个字节;对应的,64位计算机的字长为64位,即8个字节。计算机系统对基本类型数据在内存中存放的位置有限制,要求这些数据的起始地址的值是某个数k的倍数,这就是所谓的内存对齐,而这个k则被称为该数据类型的对齐模数(alignment modulus)。

    结构的存储分配

    编译器按照结构体成员列表的顺序为每个成员分配内存,当存储成员时需要满足正确地边界对齐要求时,成员之间可能出现用于填充地额外内存空间。32位系统每次分配字节数最多为4个字节,64位系统分配字节数最多为8个字节。

    以下图表是在不同系统中基本类型数据内存大小和默认对齐模数:

    注:此外指针所占内存的长度由系统决定,在32位系统下为32位(即4个字节),64位系统下则为64位(即8个字节)。

    0818b9ca8b590ca3270a3433284dd417.png

    没有#pragma pack宏的对齐

    对齐规则

    结构体的起始存储位置必须是能够被该结构体中最大的数据类型所整除。

    每个数据成员存储的起始位置是自身大小的整数倍(比如int在32位机为4字节,则int型成员要从4的整数倍地址开始存储)。

    结构体总大小(也就是sizeof的结果),必须是该结构体成员中最大的对齐模数的整数倍。若不满足,会根据需要自动填充空缺的字节。

    结构体包含另一个结构体成员,则被包含的结构体成员要从其原始结构体内部最大对齐模数的整数倍地址开始存储。(比如struct a里存有struct b,b里有char,int,double等元素,那b应该从8的整数倍开始存储。)

    结构体包含数组成员,比如char a[3],它的对齐方式和分别写3个char是一样的,也就是说它还是按一个字节对齐。如果写:typedef char Array[3],Array这种类型的对齐方式还是按一个字节对齐,而不是按它的长度3对齐。

    结构体包含共用体成员,则该共用体成员要从其原始共用体内部最大对齐模数的整数倍地址开始存储。

    现在给出一个结构体,我们针对

    win-32

    Linux-32

    进行分析;例

    1:

    struct MyStruct

    {

    char a;

    int b;

    long double c;

    };

    解答:

    win-32

    位系统下:

    由上图可知该结构体的最大对齐模数为sizeof(long double)=8;假设MyStruct从地址空间0x0000开始存放。char为1个字节,所以a存放于0x0000中;int为4个字节,根据规则,b存储的起始地址必须为其对齐模数4的整数倍,所以a后面自动填充空缺字节空间0x0001-0x0003,因此b存放于0x0004-0x0007中。long double是8个字节,由于32位系统每次最多分配4个字节,则首先分配0x0008-0x000B,由于不够存储空间,则继续分配0x000C-0x000F,所以c存储在0x0008-0x000F中,由于此时总存储空间为4+4+8=16;则16满足最大对齐模数sizeof(long double)=8的整数倍;因此,sizeof(MyStruct)=16个字节。

    Linux-32位系统下:

    由上图可知该结构体的最大对齐模数为4;假设MyStruct从地址空间0x0000开始存放。char为1个字节,所以a存放于0x0000中;int为4个字节,根据规则,b存储的起始地址必须为其对齐模数4的整数倍,所以a后面自动填充空缺字节空间0x0001-0x0003,因此b存放于0x0004-0x0007中。long double是12个字节,由于32位系统每次最多分配4个字节,则首先分配0x0008-0x000B,由于不够存储空间,则继续分配0x000C-0x000F,仍然不满足存储c,则继续分配0x0010-0x0013,所以c存储在0x0008-0x0013中,由于此时总存储空间为4+4+12=20;则20满足最大对齐模数4的整数倍;因此,sizeof(MyStruct)=20个字节。

    注:以下的所有例子都是在win-32下实现

    例2:

    struct B{

    char a;

    int b;

    char c;

    };

    由上图可知该结构体的最大对齐模数为sizeof(int)=4;假设B从地址空间0x0000开始存放。char为1个字节,所以a存放于0x0000中;int为4个字节,根据规则,b存储的起始地址必须为其对齐模数4的整数倍,所以a后面自动填充空缺字节空间0x0001-0x0003,因此b存放于0x0004-0x0007中。c也是char类型,所以c存放在0x0008中;此时结构体B总的大小为4+4+1=9个字节;则9不能满足最大对齐模数4的整数倍;因此在c的后面自动填充空间0x0009-0x000B,使其满足最大对齐模数的倍数,最终结构体B的存储空间为0x0000-0x000B;则sizeof(B)=12个字节。

    例3:空结构体

    struct C{

    };

    sizeof(C) = 0

    sizeof(C)

    ;

    C

    为空结构体,在

    C

    语言中占

    0

    字节,在

    C++

    中占

    1

    字节。

    例4:结构体有静态成员

    struct D{

    char a;

    int b;

    static double c; //静态成员

    };

    静态成员变量存放在全局数据区内,在编译的时候已经分配好内存空间,所以对结构体的总内存大小不做任何贡献;因此,

    sizeof(D)=4+4=8

    个字节。

    例5:结构体中包含结构体

    struct E{

    int a;

    double b;

    float c;

    };

    struct F{

    char e[2];

    int f;

    short h;

    struct E i;

    };

    在结构体E中最大对齐模数是sizeof(double)=8;且sizeof(E)=8+8+8=24个字节;在结构体F中,除了结构体成员E之外,其他的最大对齐模数是sizeof(int)=4;又因为结构体E中最大对齐模数是sizeof(double)=8;所以结构体F的最大对齐模数取E的最大对齐模数8;因此,sizeof(F)=4+4+8+24=40个字节。

    例6:结构体包含共用体

    union union1

    {

    long a;

    double b;

    char name[9];

    int c[2];

    };

    struct E{

    int a;

    double b;

    float c;

    union1 MyUnion;

    };

    共用体中的最大对齐模式是

    sizeof(double)=8

    ;则

    sizeof(union1)=16

    ;结构体

    E

    的最大对齐模数也是

    8

    ;则

    sizeof(E)=8+8+8+16=40

    个字节。

    例7:结构体包含指针成员

    typedef struct A{

    char a;

    int b;

    float c;

    double d;

    int *p;

    char *pc;

    short e;

    }A;

    结构体包含的指针成员的大小根据系统类型决定,由于这里是在win-32位系统下分析,则指针大小为4个字节;因此,结构体A的最大对齐模数为sizeof(double)=8;则sizeof(A)=4+4+8+8+4+4+8=40个字节。

    存在#pragma pack宏的对齐

    #pragma pack (n) //编译器将按照n个字节对齐

    #pragma pack () //取消自定义字节对齐方式

    对齐规则

    结构,联合,或者类的数据成员,第一个放在偏移为0的地方,以后每个数据成员的对齐,按照#pragma pack指定的数值和自身对齐模数中较小的那个。

    例8:按指定的对齐模数

    #pragma pack (2) /*指定按2字节对齐*/

    struct G{

    char b;

    int a;

    double d;

    short c;

    };

    #pragma pack () /*取消指定对齐,恢复缺省对齐*/

    在结构体

    G中成员变量的最大对齐模数是sizeof(double)=8;又因为指定对齐模数是2;所以取其较小者2为结构体G的最大对齐模数;则sizeof(G)=2+4+8+2=16;由于16是2的整数倍,则不需要填充。

    总结

    在分析结构体字节对齐时,首先确定有没有利用#pragma pack()宏定义指定对齐模数;根据情况对应上面进行两种情况分析,针对不同的系统会得到不同的结果。

    参考资料:

    展开全文
  • struct结构体字节对齐

    2021-05-16 02:43:10
    //--------------------------------------------------------本文目录结构|-为什么结构体内存对齐|-结构体内存对齐规则|-具体举例//--------------------------------------------------------一、为什么结构体内存...

    //--------------------------------------------------------

    本文目录结构

    |-为什么结构体内存对齐

    |-结构体内存对齐规则

    |-具体举例

    //--------------------------------------------------------

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

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

    a4c26d1e5885305701be709a3d33442f.png

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

    二、结构体内存对齐的规则【未指定#pragma pack时】

    a.第一个成员起始于0偏移处;

    b.每个成员按其类型大小和指定对齐参数n中较小的一个进行对齐;

    下表是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

    模数

    1

    2

    4

    4

    4

    8

    8

    8

    Linux-32

    长度

    1

    2

    4

    4

    4

    8

    8

    12

    模数

    1

    2

    4

    4

    4

    4

    4

    4

    Linux-64

    长度

    1

    2

    4

    8

    4

    8

    8

    16

    模数

    1

    2

    4

    8

    4

    8

    8

    16

    c.结构体总长度必须为所有对齐参数的整数倍;

    d.对于数组,可以拆开看做n个数组元素。

    三、具体举例

    例1:struct {short a1;short a2;short a3;}A;struct{long a1;short a2;}B;

    sizeof(A) = 6; 这个很好理解,三个short都为2。

    sizeof(B) = 8; long为4,short为2,整个为8,因为规则b。

    例2:struct A{int a;char b;short c;};struct B{char b;int a;short c;};

    sizeof(A) = 8;sizeof(B) = 12。

    深究一下,为什么是这样,我们可以看看内存里的布局情况。

    a b cA的内存布局:1111, 1*, 11

    b a cB的内存布局:1***, 1111, 11**

    其中星号*表示填充的字节。A中,b后面为何要补充一个字节?因为c为short,其起始位置要为2的倍数,就是原则1。c的后面没有补充,因为b和c正好占用4个字节,整个A占用空间为4的倍数,也就是最大成员int类型的倍数,所以不用补充。B中,b是char为1,b后面补充了3个字节,因为a是int为4,根据原则1,起始位置要为4的倍数,所以b后面要补充3个字节。c后面补充两个字节,根据原则3,整个B占用空间要为4的倍数,c后面不补充,整个B的空间为10,不符,所以要补充2个字节。

    再看两个结构中含有结构成员的例子:

    例3:struct A{int a;double b;float c;};

    struct B{char e[2];int f;double g;short h;struct A i;};

    sizeof(A) = 24; 这个比较好理解,int为4,double为8,float为4,总长为8的倍数,补齐,所以整个A为24。

    sizeof(B) = 48; 看看B的内存布局。

    e f g h iB的内存布局:11* *, 1111, 11111111, 11 * * * * * *, 1111* * * *, 11111111, 1111 * * * *

    i其实就是A的内存布局。i的起始位置要为24的倍数,所以h后面要补齐。

    例4:struct S1

    {

    short a;

    long b;

    };

    struct S2

    {

    char c;

    struct S1 d;

    double e;

    };

    在Dev c++中,默认的是8字节对齐。我们分析下在4字节对齐的情况下输出的是,S2是20,S1是8,分析如图:

    a4c26d1e5885305701be709a3d33442f.png

    在4字节对齐的情况中,有一个问题值得注意:就是图中画1的地方。这里面本应short是可以上去的。但是对于结构体中的结构体一定要十分警惕,S1是一体的,short已经由于long进行了内存对齐,后面还空了两个字节的内存,其实此时的short已经变成了4个字节了!!!即结构体不可拆,不管是多少字节对齐,他们都是一体的。所有的圈都变成了叉。所以说结构体只能往前篡位置,不能改变整体。

    我们在分析一些8字节对齐的情况,如图:

    a4c26d1e5885305701be709a3d33442f.png

    以上讲的都是没有#pragma pack宏的情况,如果有#pragma pack宏,对齐方式按照宏的定义来。比如上面的结构体前加#pragma pack(1),内存的布局就会完全改变。sizeof(A) = 16; sizeof(B) = 32;

    有了#pragma pack(1),按1字节对齐。没错,这不是理想中的没有内存对齐的世界吗。

    a b cA的内存布局:1111, 11111111, 1111

    e f g

    h iB的内存布局:11, 1111, 11111111, 11 , 1111, 11111111, 1111

    那#pragma pack(2)的结果又是多少呢?#pragma pack(4)呢?留给大家自己思考吧,相信没有问题。

    展开全文
  • 结构体字节对齐问题

    2021-04-14 14:28:14
    文章目录一、结构体必知知识二、结构体字节对齐有什么用?三、如何进行内存对齐?1.对齐规则:按结构体中最长类型字节为单位2.结构体嵌套的情况3.存在指定字节对齐单位的情况(#pragma pack)总结 一、结构体必知知识...
  • c++之结构体字节对齐

    2021-09-13 17:31:23
    1.什么是字节对齐 现代计算机中,内存空间按照字节划分,理论上可以从任何起始地址访问任意类型的变量。但实际中在访问特定类型变量时经常在特定的内存地址访问,这就需要各种类型数据按照一定的规则在空间上排列,...
  • 结构体字节对齐属于老生常谈的问题,看似简单,但却很容易忘记。而且因为结构体使用的普遍性,使得字节对齐也成为了一个不得不谈的话题。之前在C语言作业中有相应的题目,总结了一番,写下了这篇博客。最近抽出时间...
  • 在c语言的结构体里面一般会按照某种规则去进行字节对齐。 我们先看一段代码: struct st1 { char name; double age; char sex; }; //32位下 sizeof(struct st1) = 16 //64位下 sizeof(struct st1) = 24 struct st...
  • 32. 结构体字节对齐

    2021-01-01 10:05:06
    对于结构图,上面分配的空间是8字节,不是5. 这就涉及到字节对齐的问题。 效率问题:牺牲一点时间,换取CPU执行的效率,流水线不需要调整字节长度了。
  • 借复盘工作中遇到的一个实际难题,来对结构体字节对齐技能点的学习与梳理
  • 结构体字节对齐详解 C++/C 常见的数据类型: bool short (short int) int long (long int) long long (long long int) float double char Type* (指针类型) 对于char,short,long,long long都有无符号类型,无...
  • C语言结构体字节对齐

    2021-02-08 14:34:16
    对C语言结构体进行字节对齐的合理排列并不仅仅可以节省非常大的空间,最重要的是体现个人编程的良好习惯。 以N对齐方式 所谓的以N对齐方式纯属我瞎掰的,哈哈。 所有的结构体成员在定义结构体分配内存时都要与所有...
  • 1.什么是字节对齐在c语言的构造体外面个别会依照某种规定去进行字节对齐。咱们先看一段代码:struct st1{char name;double age;char sex;};//32位下 sizeof(struct st1) = 16//64位下 sizeof(struct st1) = 24struct...
  • 结构体字节对齐
  • 在有些编程的时候,很多简单的数据类型之间有关系,如一个学生...前面我们分析了int,float多个字节字节的排列顺序。同样在结构体当中是由很多的基本数据类型组合起来的,这些里面也有些规则,在定义一个结构体后对...
  • 我们来一个例子来看,结构体对齐后,结构体大小的计算 #include<iostream> using namespace std; #pragma pack(2) struct Test { int a; char b; short c; char d; }; int main() { Test a; cout <&...
  • C/C++结构体字节对齐问题1 前言2 举例3.其他 1 前言 对于结构体字节对齐问题,我发现我一直有一个误区,正是这个误区,给我带来了很大困扰,今天突然顿悟,在此记录一下,希望能帮到大家。 2 举例 #include <...
  • 32 位 Linux 下的结构体以四字节进行对齐。如果我们不希望以四字节对齐。可以选择使用 __attribute__((packed)) 对结构体进行修饰。 例如: #include <stdio.h> struct test1 { unsigned char a; ...
  • 指定结构体字节对齐方式 #pragmapack(push,1) typedefstruct { intb; chara; }struct_t1; #pragmapack(pop)
  • 字节对齐的准则:其实字节对齐的细节和具体编译器实现相关,但一般而言,满足三个准则:1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;2)结构体每个成员相对于结构体首地址的偏移量都是成员大小的整数...
  • 0x00简介首先要知道结构体对齐规制1.第一个成员在结构体变量偏移量为0的地址处2.其他成员变量对齐到某个数字的整数倍的地址处对齐数=编辑器默认的一个对齐数与该成员大小的较小值vs中默认的值为8gcc 没有默认就是...
  • 结构体变成强制单字节对齐 #pragma pack(push,1) … … … #pragma pack(pop)

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 41,253
精华内容 16,501
关键字:

结构体字节对齐