c++11总结10——内存对齐

1. 内存对齐的概念

内存对齐是一个数据类型所能存放的内存地址的属性。这个属性是一个无符号整数,并且这个整数必须是2的N次方(1、2、4、8、、、1024、、、)。

例如当我们说一个数据类型的内存对齐为8时,是指这个数据类型所定义出来的所有变量的内存地址都是8的倍数。

2. 自然对齐的概念

当一个基本数据类型的对齐属性和这个数据类型的大小相等时,称为自然对齐。例如一个4字节大小的int型数据,默认情况下字节对齐是4。

3. 为什么需要内存对齐?

1) 并不是每一个硬件平台都能随便访问任意位置的内存的。某些平台(例如Alpha等)若读取的数据未内存对齐,将拒绝访问或抛出异常。

2)将一个int放在奇数内存(例如1)位置上,如果想把4个字节读出来,32位CPU需要读取两次。如下例子所示:

preview

如果按照4字节对齐后,一次就可以读取出来了,提升了效率。

4. 内存对齐示例

#include <iostream>
using namespace std;

struct
{
	int i;
	char c1;
	char c2;
}Test1;

struct {
	char c1;
	int i;
	char c2;
}Test2;

struct {
	char c1;
	char c2;
	int i;
}Test3;

int main()
{
	cout << sizeof(Test1) << endl;
	cout << sizeof(Test2) << endl;
	cout << sizeof(Test3) << endl;

	system("pause");
    return 0;
}

执行结果: 

12

8

本例测试环境为vs2015,默认4字节对齐,分析如下所示:

preview

5. 堆内存对齐

malloc一般使用当前平台默认的最大内存对齐数对齐内存,比如,MSVC在32位下一般为8字节对齐,64位下则是16字节对齐。如果我们自定义的内存对齐超出了这个范围,则不能直接使用malloc获取内存。

但当我们有需求需要分配一块具有特定内存对齐的内存块时,在MSVC下应使用_aligned_malloc,gcc下一般使用memalogn等函数。

实现一个简易的aligned_malloc:

inline void* aligned_malloc(size_t size, size_t alignment)
{
	//检查alignment是否为2^N
	assert(!(alignment & (alignment - 1)));

	//计算出最大的offset,sizeof(void*)是为了存储原始指针地址
	size_t offset = sizeof(void*) + (--alignment);

	//分配一块带offset的内存
	char *p = static_cast<char*>(malloc(offset + size));
	if (!p)
		return nullptr;

	//通过"& (~alignment)"把多计算的offset减掉
	void* r = reinterpret_cast<void*>(reinterpret_cast<size_t>(p + offset) & (~alignment));

	//将r当作一个指向void*的指针,在r当前地址前面放入原始地址
	static_cast<void**>(r)[-1] = p;

	//返回经过对齐的内存地址
	return r;
}

inline void align_free(void* p)
{
	//还原回原始地址,并free
	free(static_cast<void**>(p)[-1]);
}

6. 利用alignas指定内存对齐的大小

例如:

alignas(32) long long a = 0;

#define XX 1
struct alignas(XX)MyStruct {};

注意:alignas只能改大不能改小,如果需要改小,需要使用#prama pack(n),n为对齐字节数。

c++11中与#prama等价的为_Prama(微软暂不支持,vs中暂不能使用);


_Pragma("pack(1)")
struct MyStruct
{
	char a;
	int b;
	short c;
	long d;
};
_Pragma("pack()")

7. 利用alignof和std::alignment_of获取内存对齐的大小

struct MyStruct
{
	char c;
	int a;
	double b;
};

int main()
{
	//获取内存对齐的大小
	cout << alignof(MyStruct) << endl;  //8

	int alignSize = std::alignment_of<MyStruct>::value;
	cout << alignSize << endl;  //8

	system("pause");
    return 0;
}

8. std::max_align_t和std::align操作符

1)std::max_align_t用来返回当前平台的最大默认内存对齐类型。

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

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

std::align的原型为:

void* align( std::size_t alignment,

             std::size_t size,
             void*& ptr,

             std::size_t& space );

char buf[] = "-----------------";
void* p = buf;
std::size_t space = sizeof(buf) - 1;
std::align(alignof(int), sizeof(char), p, space);

在buf这个大内存块中,指定内存对齐为slignof(int),找一块sizeof(char)大小的内存,并且在找到这块内存后将地址放入p,将buf从p开始的长度当入space。

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C++中的字节对齐(内存对齐)是指在分配内存时,将变量或结构体的起始地址对齐到特定的字节边界。这样做有助于提高内存访问的效率和性能。字节对齐的规则可以通过编译器选项或特定的关键字进行控制。 以下是关于C++字节对齐的一些重要概念和规则: 1. 默认对齐: - 编译器会使用默认的对齐规则来分配内存。通常,默认对齐值是被编译器设置的,一般为结构体成员中最大的对齐值。 2. 对齐值: - 对齐值是指要求变量或结构体的起始地址必须是该值的倍数。常见的对齐值有1、2、4、8等。 3. 对齐修饰符: - C++11引入了对齐修饰符 `alignas`,允许开发者显式地指定变量或结构体的对齐值。 4. 结构体字节对齐: - 结构体的字节对齐规则是,结构体的起始地址必须是其成员中最大对齐值的倍数。 - 编译器会在结构体成员之间插入填充字节,以保证对齐要求。 5. 类对象字节对齐: - 类对象的字节对齐规则与结构体类似,但还受到继承关系的影响。 - 派生类的起始地址必须满足其成员的对齐要求,并且满足其基类中最大对齐值的倍数。 为了控制字节对齐,可以使用编译器提供的特定选项(如`#pragma pack`)或关键字(如`alignas`)。具体的字节对齐规则和选项可能因编译器和平台而异,因此在编写代码时最好参考特定编译器的文档。正确的字节对齐可以提高内存访问性能,并确保与其他代码或外部系统的兼容性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值