c++内存对齐

内存对齐的原因

为了提高程序的性能,数据结构(尤其是栈)应该尽可能地在自然边界上对齐。因为为了访问未对齐的内存,CPU需要作两次内存访问。然而,对齐的内存访问仅需要一次访问。

内存对齐的规则

1、数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的对齐按照 #pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行对齐。

2、结构(或联合)的整体对齐规则:在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较大的那个进行。

普通结构体

一般c++编译器默认的内存的对齐值为8个字节。对于下面的TEST结构体。因为其第一个字段为char所占字节为1,而第二个字段为int在32位系统中占4个字节,默认对齐值为8,宏观对齐值(结构体中最大的成员的长度)为4。因为char cNum1的字节大小为1地址为0x00AFFC50,所以其对齐值为1。int cNum2占4个字节所以对齐值为4,所以其首地址要能被4整除所以0x00AFFC51-0x00AFFC53用0xCC填充用来对齐。结构体总体大小为实际宏观对齐值与默认对齐值中较大的那个,所以sizeof(TEST)的大小为0x8.

结构体中含有数组

如果结构体宏含有数组则用数组的类型的大小参与对齐值的计算,不能按数组的总大小参与对齐值的运算。

结构体中含有结构体

如果结构体中含有结构体则应用结构体的对齐值参与对齐值的运算,而不能用结构体的大小参与对齐值的运算。

内存没对齐进行错位访问数据

DWORD * pDword = new DWORD[10]();
pDword = (DWORD*)((BYTE*)pDword + 1);

DWORD a = *pDword;				//进行了错位访问数据

x86CPU的EFLAGS寄存器的AC标志位是对齐检查标志位,其默认是为0的,这时cpu进行错位访问数据(访问未对齐的数据)时需要CPU多执行一些操作,比访问对齐的数据效率要低但是并不会产生异常。如果AC标志位为1则一旦CPU进行错位访问时就会产生一个异常,但是对于x86-64处理器来说,CPU会默认处理这种错误。但是对于IA-64处理器来说其CPU不能处理这种错误会将这种错误传递给操作系统,Windows操作系统会产生EXCEPTION_DATATYPE_MISALINGNMENT异常。

处理IA-64处理器上错位访问数据

让操作系统处理

SetErrorMode(SEM_NOALIGNMENTFAULTEXCEPT);

通过调用在进程中调用SetErrorMode并传入参数SEM_NOALIGNMENTFAULTEXCEPT来让操作系统修正此进程中所有的错位数据的访问,一旦改变该标志就无法在进程生命周期再次改变它,且这样进程可能会遇到严重的性能下降。

让编译器处理

Microsoft Visual C/C++编译器利用__unaligned关键字修饰可能会错位访问数据的指针,从而生成额外的指令来访问数据。

DWORD * pDword = new DWORD[10]();
pDword = (DWORD*)((BYTE*)pDword + 1);

DWORD a = *(__unaligned DWORD *)pDword;				//使用__unaligned关键字访问数据

参考《Windows核心编程》

posted @ 2020-10-13 19:31  怎么可以吃突突  阅读(584)  评论(0编辑  收藏  举报