记录下最近工作中遇到的坑,其中之一便是C/C++里面的内存对齐。
Table of Contents
为什么有内存对齐
- 为了性能
- 由于内存硬件上的组织方式(具体看这篇介绍内存对齐的文章),从内存中读取数据时往往是一次性读取4字节、8字节,为了能够尽可能地提升读写内存的效率,于是有了内存对齐的概念。
- 像一些SSE之类的指令对数据也有着对齐上的要求。
- CPU寻址支持
比如在某些平台上如果你把int存放在不是4的倍数开头的地址,那么你去存取这个int的时候是会报Bus Error(SIGBUS)的。写过移动端sdk的人应该对此深有体会。在x86平台上则没有这种问题。
理解内存对齐讲了什么
对于基本数据类型,这些数据的内存地址必须是其对齐长度的倍数:
Data Type | 32-bit (bytes) | 64-bit(bytes) |
---|---|---|
char | 1 | 1 |
short | 2 | 2 |
int | 4 | 4 |
long | 8 | 8 |
float | 4 | 4 |
double | 8 | 8 |
long long | 8 | 8 |
long double | 4 | 16 |
Any pointer | 4 | 8 |
对于结构体,其对齐长度是其成员里对齐长度最大的那一个;
为了照顾内存对齐,结构体的内部和末尾会产生padding;
比如intel这篇文档里的例子:
struct s1
{
char a;
short a1;
char b1;
float b;
int c;
char e;
double f;
};
struct s2
{
double f;
float b;
short a1;
char a,b1,e;
};
结构体s1要占用32个字节,内部有11个字节的padding,而成员相同的结构体s2只占用24个字节,只有末尾的3个字节的padding。
malloc函数返回值是一个按照max_align_t对齐的地址,这个max_align_t一般是8和16,按max_align_t对齐保证了所有基本数据类型都可以放在malloc返回地址开头的内存里;
编程语言为我们提供了关于内存对齐的哪些工具
踩过内存对齐的哪些坑
- 写移动端sdk的人经常会遇到没注意内存对齐,导致报SIGBUS的问题;
为了写出更portable和健壮的代码,需要留意数据类型的对齐要求; - 因为忽略了内存对齐的影响,在手动计算结构体大小和成员变量的偏移量时经常出错;
这种错误挺坑的,可能你本意是打算存取某个成员变量的,实际上却操作的是作为padding的无用数据,在序列化反序列化传递数据时就会遇到这种坑;
解决办法就是要么牢记内存对齐的规则,要么请熟练使用sizeof和offsetof两个宏;
理解了内存对齐后可以怎样把程序写的更好
- 当然是避免上面列出的那些坑;
- 参照intel文档的那个例子,合理安排成员变量的顺序节约空间;
- 对于某些结构体,手动设置其内存对齐长度,提升其在cache上的性能;
近期评论