一、内存对齐
1.何为内存对齐
对于代码:
#include <iostream>
using namespace std;
struct HowManyBytes{
char a;
int b;
};
int main() {
cout << "sizeof(char): " << sizeof(char) << endl;
cout << "sizeof(int): " << sizeof(int) << endl;
cout << "sizeof(HowManyBytes): " << sizeof(HowManyBytes) << endl;
cout << endl;
cout << "offset of char a: " << offsetof(HowManyBytes, a) << endl; //0
cout << "offset of int b: " << offsetof(HowManyBytes, b) << endl; //4
return 0;
}
// 编译选项:g++ -std=c++11 8-1-1.cpp
代码中成员a占1个字节,成员b占4字节,而通过offsetof(用于获取成员变量在类中的偏移量),查看a,b成员位置时,b的位置为4而不是1,这是因为C/C++对数据结构有着对齐要求。
内存对齐是一个整数,意味着该数据成员地址只能位于内存对齐的倍数上,如b位于4而非1。而对齐之间的未使用空间被称为填充数据,如a之后的1,2,3三个字节内容。
对齐方式
2.内存对齐的优势
内存对齐主要有两点优势:
- 跨平台,有些平台要求内存对齐,否则无法运行
- 性能,内存对齐有利于提高数据缓存速度。
二、C++11对于内存对齐的支持
1.alignof
C++11支持操作符alignof获取定义完整类型的内存对齐要求,而不支持获取不完整类型或变量对齐值。
#include <iostream>
using namespace std;
class InComplete;
struct Completed{};
int main(){
int a;
long long b;
auto & c = b;
char d[1024];
// 对内置类型和完整类型使用alignof
cout << alignof(int) << endl // 4
<< alignof(Completed) << endl; // 1
// 对变量、引用或者数组使用alignof
cout << alignof(a) << endl // 无法编译
<< alignof(b) << endl // 无法编译
<< alignof(c) << endl // 8,无法编译
<< alignof(d) << endl; // 1, 无法编译
// 本句无法通过编译,Incomplete类型不完整
// cout << alignof(Incomplete) << endl;
}
// 编译选项:g++ -std=c++11 8-1-4.cpp
2.alignas
除了获取内存对齐值外,C++11提供对齐描述符alignas()设置内存对齐值,需要注意的是,内存对齐值设置有着一定的要求:
- 首先,设置值必须是2的幂
- 其次各平台拥有基准对齐值:std::max_align_t,alignas设置值不允许小于此值(想要设置可以通过#pragma pack(n)或aligns、__declspec(align(#))),
- 而大于基准对齐值的则称为扩展对齐,扩展对齐也受平台限制,溢出后认为该程序是不规范的。
- 同一个变量的内存对齐值设置要求一致。
alignas(double) void f(); // 错误:alignas不能修饰函数
alignas(double) unsigned char c[sizeof(double)]; // 正确
extern unsigned char c[sizeof(double)];
alignas(float)
extern unsigned char c[sizeof(double)]; // 错误:不同对齐方式的变量定义
下面是一个容量固定但大小随使用数据变化的例子:
#include <iostream>
using namespace std;
struct alignas(alignof(double)*4) ColorVector {
double r;
double g;
double b;
double a;
};
// 固定容量的模板数组
template <typename T>
class FixedCapacityArray {
public:
void push_back(T t) { /* 在data中加入t变量 */}
// ...
// 一些其他成员函数、成员变量等
// ...
char alignas(T) data[1024] = {0};
//int length = 1024 / sizeof(T);
};
int main() {
FixedCapacityArray<char> arrCh;
cout << "alignof(char): " << alignof(char) << endl;
cout << "alignof(arrCh.data): " << alignof(arrCh.data) << endl;
FixedCapacityArray<ColorVector> arrCV;
cout << "alignof(ColorVector): " << alignof(ColorVector) << endl;
cout << "alignof(arrCV.data): " << alignof(arrCV.data) << endl;
return 1;
}
输出为:
alignof(char): 1
alignof(arrCh.data): 1
alignof(ColorVector): 32
alignof(arrCV.data): 32
而如果移除17行的alignas(T),则输出为:
alignof(char): 1
alignof(arrCh.data): 1
alignof(ColorVector): 32
alignof(arrCV.data): 1
很显然,这样在push_back时,将一个对齐值为32的ColorVector塞到对齐值只有1的arrCV.data中,可能会导致一些性能上的丢失(虽然这里我实验了一下也没有什么区别~~~)
3.align
C++11还提供函数:
void* align( std::size_t alignment, std::size_t size, void*& ptr, std::size_t& space );
用于设置指定地址ptr后指定大小space的内存对齐值。
4.aligned_storage和aligned_union
template< std::size_t Len, std::size_t Align = /*default-alignment*/ >
struct aligned_storage;
template< std::size_t Len, class... Types >
struct aligned_union;
感觉C++11这两个属性还不成熟,后面的标准进一步完善了。