• C/C++内存(字节)对齐


    1.什么是字节对齐:

    (1)对于基本数据类型,如果一个变量占用n个字节,则该变量在内存中的起始地址必须是n的整数倍,即:存放起始地址 % n = 0。比如,int型变量占用4个字节,则int a; 变量a在内存中的起始地址必须是4的整数倍。
    (2)对于结构体类型,那么结构体的起始地址是其最宽数据类型成员的整数倍。比如下面的结构体:

    struct Stu{
    	int a;
    	char b;
    	double c;
    }
    //定义结构体变量
    struct Stu st;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    对于结构体变量st,其成员变量占用字节数最多的变量类型是double,占用8个字节,所以变量st在内存中的起始地址是8的整数倍。

    2.为什么要内存对齐

    为了提高程序的执行效率。
    具体来说就是访问内存速度更快。实际内存读取时,内存单元每n个一组,一次读一组。
    实质上是空间换时间。

    3.字节对齐的原则:

    这涉及到了有没有杂注: #pragma pack(n)的问题,可以使用#pragma pack(n)来添加杂注,改变结构体成员的对齐方式。VS中默认杂注为8,可以使用

    #pragma pack(show)
    
    • 1

    来查看默认的杂注,在程序中添加上面的这行代码,然后生成解决方案,就可以查看默认的杂注。如下:

    warning C4810: 杂注 pack(show) 的值 == 8
    
    • 1

    当然,也可以手动设置,设置方式为:

    #pragma pack(n)
    
    • 1

    其中n用来设定变量以n字节对齐方式,可以设定的值包括:1、2、4、8,VS中默认是8
    若需要取消强制对齐方式,可用命令

    #pragma pack()
    
    • 1

    不过需要注意的是:#pragma pack(n)无法影响结构体开始位置,只能影响结构体中成员的对齐方式

    字节对齐具体原则

    • 1.从偏移为0的位置开始存储;

    • 2.如果没有定义杂注#pragma pack(n)

      • 2.1 sizeof的最终结果必然是结构体内部最大成员的整数倍,不够补齐;
      • 2.2 结构体内部各个成员的首地址必然是自身大小的整数倍;
    • 3.如果定义了#pragma pack(n)

      • 3.1 sizeof的最终结果必然是min(n,结构体内部最大成员)的整数倍,不够补齐;

      • 3.2 结构体内部各个成员的首地址必然是min(n,自身大小)的整数倍。

    4.实例

    下面结合实例分析,来了解字节对齐。
    说明:我使用win10,VS编译器。int占4个字节,char占1个字节,double占8个字节
    首先,不使用杂注的情况。

    不使用杂注

    #include<stdio.h>
    
    //sizeof(S1)=24,这个是容易理解的
    struct S1{		//没有杂注
    	int i;		//0-3
    	char j;		//4-7
    	int a;		//8-15
    	double b;	//16-23
    };
    
    //sizeof(S2)=24,如果按照单个成员变量来分析,结构体占用是20个字节,
    //但是因为结构体的起始地址是最宽成员类型的整数倍,即是8的整数倍,所以sizeof(S2)=24
    struct S2{		//没有杂注
    	int i;		//0-3
    	char j;		//4-7
    	double b;	//8-15
    	int a;		//16-19
    };
    
    int main() {
    	struct S1 s1;
    	struct S2 s2;
    	printf("%d\n", sizeof(s1));
    	printf("%d\n", sizeof(s2));
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    分析:
    (1)对于结构体S1,int i占用4个字节,并且存放的起始地址是4的整数倍,由于刚开始是从0开始的,自动满足,存储位置为0-3char j占用1个字节,如果没有字节对齐的话,变量j存放在位置4就行了,但是因为字节对齐,int a,也占用4个字节,且存放的起始位置是4的整数倍,所以变量a必须从位置8开始存放。所以变量j占用位置为4-7。同理,int a存放位置为8-15double b存放的位置为16-23。此时整个结构体的大小是24,正好是8的整数倍,符合结构体的对齐原则,所以sizeof(S1)=24
    (2)对于结构体S2,参照结构体S1的分析,int i存放在0-3char j存放在4-7double b存放在8-15int a存放在16-19。此时结构体大小为20个字节,但是由于结构体对齐原则(结构体的起始地址是最宽成员类型的整数倍),也就是起始位置必须是8的整数倍,所以sizeof(S2)=24,而不是等于20。
    执行结果:
    在这里插入图片描述

    #pragma pack(1)的情况

    代码:

    #include<stdio.h>
    
    struct S1{		//有杂注,n=1
    	int i;		//0-3
    	char j;		//4
    	int a;		//5-8
    	double b;	//9-16
    };
    
    struct S2{		//有杂注,n=1
    	int i;		//0-3
    	char j;		//4
    	double b;	//5-12
    	int a;		//13-16
    };
    
    int main() {
    	struct S1 s1;
    	struct S2 s2;
    	printf("%d\n", sizeof(s1));
    	printf("%d\n", sizeof(s2));
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    分析:
    (1)对于结构体S1,int i占用4个字节,并且存放的起始地址是4的整数倍,由于刚开始是从0开始的,自动满足,存储位置为0-3char j占用1个字节,由字节对齐具体原则3.2char j的首地址是min(1,1)的整数倍,也就是1的整数倍,所以存放位置为4。同理int a的首地址也是1的整数倍,存放位置为5-8double b的首地址也是1的整数倍,存放位置为9-16。此时结构体占用的空间是17,由字节对齐具体原则3.1,结构体的占用空间必须是min(1,8)的整数倍,也就是1的整数倍,所以17是符合的。
    (2)对于结构体S2,参照结构体S1的分析。
    结果:
    在这里插入图片描述

    #pragma pack(2)的情况

    代码:

    #include<stdio.h>
    
    struct S1{		//有杂注,n=2
    	int i;		//0-3
    	char j;		//4-5
    	int a;		//6-9
    	double b;	//10-17
    };
    
    struct S2{		//有杂注,n=2
    	int i;		//0-3
    	char j;		//4-5
    	double b;	//6-13
    	int a;		//14-17
    };
    
    int main() {
    	struct S1 s1;
    	struct S2 s2;
    	printf("%d\n", sizeof(s1));
    	printf("%d\n", sizeof(s2));
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    分析:
    (1)对于结构体S1,int i占用4个字节,并且存放的起始地址是4的整数倍,由于刚开始是从0开始的,自动满足,存储位置为0-3char j占用1个字节,由字节对齐具体原则3.2char j的首地址是min(1,2)的整数倍,也就是1的整数倍,所以应该存放位置为4。但是int a的首地址是min(4,2)的整数倍,也就是2的整数倍,所以char j的存放位置为4-5int a的存放位置为6-9double b的存放位置为10-17。此时结构体的大小为18,也符合字节对齐具体原则3.1。所以sizeof(S1)=18
    (2)对于结构体S2,参照结构体S1的分析。
    结果:
    在这里插入图片描述

  • 相关阅读:
    Java算法(三): 判断两个数组是否为相等 → (要求:长度、顺序、元素)相等
    Python+Appium+Pytest+Allure实战APP自动化测试!
    机器学习笔记之配分函数(一)对数似然梯度
    SD6.22集训总结
    MySQL基础篇【第四篇】| 连接查询、子查询(嵌套)
    RenderThread 崩溃问题调查
    【MySQL】表的增删改查
    BFS:845. 八数码
    5.2 加载矢量图层(delimitedtext,spatialite,wfs,memory)
    结构型模式-适配器模式
  • 原文地址:https://blog.csdn.net/weixin_45003868/article/details/125607215