• 【C语言】结构体内存对齐机制详解


    一、前言

    在讲解结构体内存对齐机制之前,我们先来看1个例子:

    typedef struct
    {
        char sex;       //  性别
        int id;         //  学号
        char name[20];  //  姓名
        float score;    //  成绩
        char addr[30];  //  地址
    }STU;
    
    int main(){
        STU student1;
        printf("%d\n",sizeof(student1));//  打印结构体变量所占内存长度
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    以上代码示例中定义了1个结构体student1,包含5个不同数据类型的成员,分别占用内存空间:
    char sex:1 Byte
    int id:4 Byte
    char name[20]:20 Byte
    float score:4 Byte
    char addr[30]:30 Byte
    根据结构体特性,结构体变量所占内存长度等于其各成员所占内存长度之和,因此这里打印的sizeof(student1)是否等于1+4+20+4+30=59呢?运行程序后结果如下:

    size of student1: 64
    
    • 1

    为什么这里结构体变量student1所占内存长度是64而不是59呢?因为有结构体内存对齐机制的存在。这是很多新手程序猿很容易走进的一个误区,接下来我们一起来了解结构体对齐机制。

    二、结构体内存对齐规则

    1. 结构体第一个成员的地址即为该结构体变量的地址。
    2. 结构体成员内存对齐时,其存放的内存单元大小为min{有效对齐值,指定对齐值}的最小整数倍。
      注意:
      自身对齐值: 结构体变量里每个成员自身所占内存大小
      指定对齐值: 使用宏定义#pragma pack(N) 其中N的值即为指定对齐值。N必须是2的幂次方,例如:1,2,4,8等,如果没有使用宏定义指定对齐值,则指定对齐值取决于操作系统位数,32位操作系统默认指定对齐值为4,64位操作系统默认指定对齐值为8
      有效对齐值: 有效对齐值为min{自身对齐值,指定对齐值}。
    3. 结构体总占用内存大小为min{成员最大自身对齐值,指定对齐值}的整数倍。

    三、实例解析

    以下示例代码均在64位Windows操作系统下运行
    1、使用默认指定对齐值
    使用文章前言的代码分析:

    #include 
    
    typedef struct
    {
        char sex;       //  性别
        int id;         //  学号
        char name[20];  //  姓名
        float score;    //  成绩
        char addr[30];  //  地址
    }STU;
    
    int main(){
        STU student1;
        printf("size of student1: %d\n",sizeof(student1));    //  打印结构体变量所占内存长度
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    成员自身对齐值指定对齐值有效对齐值
    sex18min{1,8}
    id48min{4,8}
    name[20]18min{1,8}
    score48min{4,8}
    addr[30]18min{1,8}

    结构体总体对齐时,应按照成员自身对齐值的最大值的整数倍对齐,这里程序在没有宏定义指定默认对齐值的情况下,对齐值就应该是min{4,8},也就是4字节,这种情况下,该结构体每一个成员开辟的存储空间都为4字节。

    成员实际占用内存大小系统开辟内存空间大小
    sex14
    id44
    name[20]2020
    score44
    addr[30]3032

    在这里插入图片描述

    成员id、name[20]、score实际占用内存大小刚好是对齐值4的整数倍,而成员sex则需要开辟4字节空间,实际只占用1字节,剩余3字节未使用;成员addr[30]需要开辟32字节空间,实际使用30字节,剩余2字节未使用。因此打印结构体变量student1所占内存大小为64字节。

    2、修改默认指定对齐值
    在上述示例基础之上修改默认指定对齐值:

    #include 
    #pragma pack(1) //  指定默认对齐值为1
    typedef struct
    {
        char sex;       //  性别
        int id;         //  学号
        char name[20];  //  姓名
        float score;    //  成绩
        char addr[30];  //  地址
    }STU;
    #pragma pack()  //  取消修改对齐值
    int main(){
        STU student1;
        printf("size of student1: %d\n",sizeof(student1));    //  打印结构体变量所占内存长度
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    此时程序输出结果为:

    size of student1: 59
    
    • 1

    以上代码使用#pragma pack(1) 将默认指定对齐值修改为1,那么结构体成员将会按照1字节做内存对齐。

    成员实际占用内存大小系统开辟内存空间大小
    sex11
    id44
    name[20]2020
    score44
    addr[30]3030

    此时打印结构体变量student1所占内存长度就为59。

    如果将默认指定对齐值修改为2呢?

    #include 
    #pragma pack(2) //  指定默认对齐值为1
    typedef struct
    {
        char sex;       //  性别
        int id;         //  学号
        char name[20];  //  姓名
        float score;    //  成绩
        char addr[30];  //  地址
    }STU;
    #pragma pack()  //  取消修改对齐值
    int main(){
        STU student1;
        printf("size of student1: %d\n",sizeof(student1));    //  打印结构体变量所占内存长度
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    程序输出结果:

    size of student1: 60
    
    • 1
    成员实际占用内存大小系统开辟内存空间大小
    sex12
    id44
    name[20]2020
    score44
    addr[30]3030

    这个时候除了成员sex之外,其余成员的自身对齐数均是2的整数倍,因此成员sex需要根据对齐数2进行内存对齐,开辟2字节空间存储,但实际只使用1字节空间,最后打印结构体变量student1的所占内存长度为60。

  • 相关阅读:
    羧甲基荧光素6-FAM修饰聚缩醛Polyacetal/HA透明质酸纳米载体6-FAM-Polyacetal|6-FAM-HA(齐岳)
    算法 之 链表
    计算机系大学生,可以通过Java做什么副业?这篇文章给你答案!
    ts1.基础类型
    [Python进阶] 消息框、弹窗:pymsgbox.alert
    「 程序员的风险控制」两三百实现医疗自由,不怕学习工作太拼生病花大钱
    动作捕捉用于验证芭蕾舞动作对脑瘫儿童的作用
    堆排序
    iptables详解:常用模块的基本使用
    Guava-字符串工具
  • 原文地址:https://blog.csdn.net/weixin_45137708/article/details/133161744