• 【C语言】【结构体的内存对齐】计算结构体内存大小,有图解


    计算结构体内存大小,需要用到结构体内存对齐的知识
    来段代码看看什么是结构体对齐

    #include
    struct S1 
    {
    	char a;
    	char b;
    	int num;
    };
    struct S2
    {
    	char a;
    	int num;
    	char b;
    };
    int main()
    {
    	printf("%zd\n", sizeof(struct S1));
    	printf("%zd", sizeof(struct S2));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    在这里插入图片描述
    两个结构体包含的变量类型相同,但计算出来的内存大小不同,这就说明两个结构体在内存中的存储方式不同

    结构体的存储规则就是:内存对齐

    1.对齐规则:

    1. 结构体的第⼀个成员对⻬到相对结构体变量起始位置偏移量为0的地址处
    2. 其他成员变量要对⻬到某个数字(对⻬数)的整数倍的地址处。 对⻬数 = 编译器默认的⼀个对⻬数 与 该成员变量⼤⼩的较⼩值。
    • VS中默认的值为8
    • Linux中没有默认对⻬数,对⻬数就是成员⾃⾝的⼤⼩
    1. 结构体总⼤⼩为最⼤对⻬数(结构体中每个成员变量都有⼀个对⻬数,所有对⻬数中最⼤的)的 整数倍。
    2. 如果嵌套了结构体的情况,嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处,结构 体的整体⼤⼩就是所有最⼤对⻬数(含嵌套结构体中成员的对⻬数)的整数倍。

    1.1先用上面的代码解释前3条

    在这里插入图片描述
    第一个元素分别存到初始位置,char占 1 个字节

    在这里插入图片描述
    char类型占 1 个字节,小于VS默认的 8 个字节,所以存char时,每个对齐数都是 1 的倍数,可以紧挨着存 1 个字节
    int 类型占 4 个字节,小于VS默认的 8 个字节,所以存 Int时,从对齐数 4 的位置开始存 4 个字节
    由于存入的顺序取决于初始化结构体中变量的顺序,所以S1中接着存了 int 类型的,S2中接着存了 char类型的
    在这里插入图片描述

    数据存完之后,检验总内存大小是否是变量中最大对齐数的整数倍。如果是,最终内存大小就决定了;如果不是,还要再占内存直到补到倍数为止。蓝色的单元格表示浪费掉的内存。

    所以最终结果:S1占 12 字节内存,S2占 8 字节内存。

    1.2嵌套结构体计算大小

    #include
    struct S3
    {
    	double d;
    	char e;
    	int i;
    };
    struct S4
    {
    	char j;
    	struct S3 s3;
    	double k;
    };
    int main()
    {
    	printf("%zd\n", sizeof(struct S3));
    	printf("%zd", sizeof(struct S4));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    根据前三条规则,可以计算出S3的内存为 16
    所以第一个printf打印出来的是 16
    在这里插入图片描述
    在这里插入图片描述

    S3中最大元素的内存是 8 个字节,所以存储S3的对齐数是 8 ,存入8 个字节;此时对齐数是 16 ,正好符合double类型内存(8个字节)的整数倍。存完double后对齐数是24。最后,因为24不是最大内存数 16 的倍数,所以从24到32之间的内存浪费掉。

    第二个printf打印的是 32
    在这里插入图片描述

    2.内存对齐的性能原因:

    数据结构(尤其是栈)应该尽可能地在⾃然边界上对⻬。原因在于,为了访问未对⻬的内存,处理器需要作两次内存访问;⽽对⻬的内存访问仅需要⼀次访问。假设⼀个处理器总是从内存中取8个字节,则地址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对⻬成8的倍数,那么就可以⽤⼀个内存操作来读或者写值了。否则,我们可能需要执⾏两次内存访问,因为对象可能被分放在两个8字节内存块中。
    总体来说:结构体的内存对⻬是拿空间来换取时间的做法。

    那么在设计结构体的时候,既要满足对齐,又要节省空间,可以将占空间小的成员尽量聚集在一起:

    #include
    struct S1
    {
    	char a;
    	char b;
    	int i;
    };
    struct S2
    {
    	char a;
    	int i;
    	char b;
    };
    int main()
    {
    	printf("%zd\n", sizeof(struct S1));
    	printf("%zd\n", sizeof(struct S2));
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在这里插入图片描述

    3.修改内存对齐数:

    #pragma这个预处理指令,可以修改默认对齐数

    #include
    #pragma pack(1) //将默认对齐数设置为 1
    struct S
    {
    	char a;
    	char b;
    	int c;
    };
    #pragma pack() //恢复为默认对齐数
    int main()
    {
    	printf("%zd\n", sizeof(struct S));
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    在这里插入图片描述

  • 相关阅读:
    C语言实现单链表
    吉时利KEITHELY2612B源表技术参数
    【git随笔,日常积累】
    sonic-ios-bridge(sib)性能监控
    【华为OD机试真题 python】 最大N个数与最小N个数的和【2022 Q4 | 100分】
    Mysql 数据恢复逻辑 基于binlog redolog undolog
    模板继承、模板中加载静态文件
    Spring Boot自动加载
    常见协议UDP和TCP详解
    centos卸载nginx
  • 原文地址:https://blog.csdn.net/Legend_6zh/article/details/133322722