【C/C++学习笔记】C++11 新特性之内存对齐

  • Post author:
  • Post category:其他


1.什么是内存对齐

举个例子介绍下内存对齐,有兴趣的小伙伴可以自己运行如下的示例,看看由char,short,int,double组成的结构体A,B,C占用的内存大小是多少?

#include <iostream>

struct A
{
    int a;
    short b;
    char c;
    double d;
};

struct B
{
    int a;
    char b;
    short c;
    double d;
};

struct C
{
    char a;
    int b;
    double c;
    short d;
};

int main()
{
	printf("%d %d %d", sizeof(A), sizeof(B), sizeof(C));
}

简单地说,每个特定平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数)。一般默认的对齐系数是4个字节。当然,程序员也可以通过预编译命令#pragma pack(n),n=1,2,4,8,16来改变这一系数,其中的n就是你要指定的“对齐系数”。

对齐规则:

(1) 结构体第一个成员的偏移量(offset)为0,以后每个成员相对于结构体首地址的 offset 都是该成员大小与有效对齐值中较小那个的整数倍,如有需要编译器会在成员之间加上填充字节。

(3) 结构体的总大小为 有效对齐值 的整数倍,如有需要编译器会在最末一个成员之后加上填充字节。

有关结构体字节对齐方式设置 #pragma pack 用法

#pragma pack (n)         // 作用:C编译器将按照n个字节对齐。
#pragma pack ()          // 作用:取消自定义字节对齐方式。


#pragma pack (push,n)   // 作用:是指把原来对齐方式设置压栈,并设新的对齐方式设置为n个字节对齐
#pragma pack(pop)        // 作用:恢复对齐状态

因此可见,加入push和pop可以使对齐恢复到原来状态,而不是编译器默认,可以说后者更优,但是很多时候两者差别不大

举个示例

#pragma pack (1)   // 设置对齐方式
struct A
{
    int a;
    char b;
    short c;
    double d;
};
#pragma pack ()   // 取消自定义对齐方式

#pragma pack (push, 1)  // 设置对齐方式
struct B
{
    int a;
    char b;
    short c;
    double d;
};
#pragma pack (pop)   // 恢复对齐方式

2.

为什么要进行内存对齐

尽管内存是以字节为单位,但是大部分处理器并不是按字节块来存取内存的.它一般会以双字节,四字节,8字节,16字节甚至32字节为单位来存取内存,我们将上述这些存取单位称为内存存取粒度.

现在考虑4字节存取粒度的处理器取int类型变量(32位系统),该处理器只能从地址为4的倍数的内存开始读取数据。

假如没有内存对齐机制,数据可以任意存放,现在一个int变量存放在从地址1开始的连续四个字节地址中,该处理器去取数据时,要先从0地址开始读取第一个4字节块,剔除不想要的字节(0地址),然后从地址4开始读取下一个4字节块,同样剔除不要的数据(5,6,7地址),最后留下的两块数据合并放入寄存器.这需要做很多工作.


简单的说内存对齐能够提高 cpu 读取数据的速度,减少 cpu 访问数据的出错性(有些 cpu 必须内存对齐,否则指针访问会出错)

上面介绍了#pragma pack()方法实现内存对齐。接下来介绍C++11中相关内存对其的方法。

3.C++11 内存对齐的新属性

3.1 alignas

alignas指定内存对其大小,有时候我们希望不按照默认的内存对齐方式来对齐,这时我们可以用alignas来指定内存对齐。

在C++11中,只要是一个编译期数值(#define, static const, template)都支持alignas,另外需要注意alignas只能改大不能改小,如果要改小可以使用上面提到的#pragma pack(1)

3.2 alignof 和 std::alignment_of

alignof用来获取内存对齐大小,用法比较简单:

  A a;
  cout << alignof(a) << endl;

alignof只能返回一个size_t,而std::alignment_of继承自std::integral_constant,拥有value_type,type,value成员

  cout << std::alignment_of<A>::value << endl;   >>>> 1
  cout << std::alignment_of<B>::value << endl;   >>>> 2

3.3 std::aligned_storage

std::aligned_storage可以看成一个内存对其的缓冲区,原型如下:

  template<std::size_t Len, std::size_t Align = /*default-alignment*/>

  struct aligned_storage;

Len表示所存储类型的sie,Align表示该类型的内存对齐大小

3.4 max_align_t 和 std::align

std::max_align_t用来返回当前平台的最大默认内存对齐类型,对于malloc返回的内存,其对齐和max_align_t类型的对齐大小应当是一致的。我们可以通过下面的方式获得当前平台的最大默认内存对齐数:

std::cout << alignof(std::max_align_t) << std::endl;

std::align用来在一大块内存中获取一个符合指定内存要求的地址

char buffer[] = "......";
void *ptr = buffer;
std::size_t space = sizeof(buffer) - 1;
std::align(alignof(int),sizeof(char),pt,space);



版权声明:本文为hallyz945原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。