本期内容主要学习一下C++内存对齐原则
什么是内存对齐原则?
内存对齐:就是编译器将程序中的每一个“数据单元”安排在字的整数倍地址指向的内存之中,学过操作系统和计算机组成原理,需要回顾一下内存地址相关知识。
这里我把百度百科的对于内存地址的部分内容拿过来供大家看一下:
我们把内存地址分为逻辑地址和物理地址,那么将某一段寄存器左移4位,然后与地址ADDR相加后被直接送到内存总线上,这个相加后的地址就是的物理地址,而程序中的这个地址就叫逻辑地址(或叫虚地址)。在80386的保护模式下,这个逻辑地址不是被直接送到内存总线,而是被送到内存管理单元(MMU)。MMU由一个或一组芯片组成,其功能是把逻辑地址映射为物理地址,即进行地址转换。
操作系统及部分特定之公用软件(如内存测试软件)等系统软件,能使用机器码的运算对象或寄存器对物理地址定址,指示CPU要求内存控制器之类的硬件设备,使用内存总线或系统总线,亦或分别之控制总线、地址总线及数据总线,运行该程序之命令。内存控制器的总线是由数条并行的线路所组成的,每条线路表示一个比特。总线的宽度因此依电脑不同,决定了可定址之存储单位数量,以及每一单位内的比特数量。
我们之所以电脑可以进行访问文件系统,都是因为每份“文件”都有与之对应的内存地址(或者说是物理地址),电脑CPU会进行访问这个地址,获取到文件在内存的存储内容。这里面就要涉及存储器、缓冲区等等概念;这里就不在一一赘述。
那么内存对齐的原则是什么呢?
结构体每个成员相对于结构体首地址的偏移量(offset) 都是该成 员大小与对齐基数中的较小者的整数倍,如有需要编译器会在成员之间加上填充字节(internalpadding)。
结构体的总大小为结构体最宽基本类型成员大小与对齐基数中的较小者的整数倍,如有需要编译器会在最末一个成员之后加上填充字节(railing padding)。
/*下段程序基于64位编译器下进行测试*/
#include <iostream>
using namespace std;
struct A
{
short var;//2字节
int var1;//8字节(内存对齐原则:填充2字节) 2(short)+2(填充)+4(int) = 8
long var2;//12字节 8+4(long)=12
char var3;//16字节(内存对齐原则:填充3字节) 12+1(int)+3(填充) = 16
string a;//48字节 16+32(string)=48
};
int main()
{
short var;
int var1;
long var2;
char var3;
string a;
A ex1;
cout<<sizeif(var)<<endl;//2 short
cout<<sizeif(var1)<<endl;//4 int
cout<<sizeif(var2)<<endl;//4 long
cout<<sizeif(var3)<<endl;//1 char
cout<<sizeif(a)<<endl;//32 string
cout<<sizeif(ex1)<<endl;//48 struct
return 0;
}
进行内存对齐的原因:
主要是因为硬件设备的问题
1.某些硬件设备只能存取对齐数据,存取非对齐的数据可能会引发异常;
2.某些硬件设备不能保证在存取非对齐数据的时候的操作是原子操作;
3.相比于存取对齐的数据,存取非对齐的数据需要花费更多的时间;
4.某些处理器虽然支持非对齐数据的访问,但会引发对齐陷阱(alignment trap);
5.某些硬件设备只支持简单数据指令非对齐存取,不支持复杂数据指令的非对齐存取。
内存对齐的好处优点在哪里?
便于在不同平台进行跨平台的开发,代码移植会很方面,有些硬件平台不能支持任意地址的数据访问,只能在某些地址处取某些特定的数据,否则会抛出异常。
同时可以提高内存访问效率,提升性能,因为在CPU读取内存时,是分块读取的。
补充知识点:
struct D{
char a;
int b;
Static double c;//静态成员
};
win32系统,静态成员变量存放在全局数据区内,在编译的时候已经分配好内存空间,所以 对结构体的总内存大小不做任何贡献:因此,sizeof(D)=4+4=8 个字节
linux下:
long在64位下是8字节吧,32位下是4字节
指针也是64位下是8字节,32位下是4字节
在64位Linux下,结构体字段默认按8字节对齐; 32位Linux 下,默认4字节对齐
vs2019 x86下string对象28字节,x64 下是40字节。
好啦,以上就是今天所学到的一个重要的C/C++基础知识点,感兴趣的小伙伴欢迎评论区留言、点赞、转发。