C语言、C++内存对齐问题详解
这也可以?
#include <iostream>
using namespace std;
struct Test_A
{
char a;
char b;
int c;
};
struct Test_B
{
char a;
int c;
char b;
};
struct Test_C
{
int c;
char a;
char b;
};
int main()
{
struct Test_A a;
memset(&a, 0, sizeof(a));
struct Test_B b;
memset(&b, 0, sizeof(b));
struct Test_C c;
memset(&c, 0, sizeof(c));
// Print the memory size of the struct
cout<<sizeof(a)<<endl;
cout<<sizeof(b)<<endl;
cout<<sizeof(c)<<endl;
return 0;
}
好了,一段简单的程序,上面的这段程序输出是什么?如果你很懂,也就会知道我接下来要讲什么了,可以略过了;如果,你不知道,或者还很模糊,请继续阅读。
这是为什么?
上面这段程序的输出结果如下(windows 8.1 + visual studio 2012 update3下运行):
// Print the memory size of the struct
cout<< sizeof(a)<<endl; // 8bytes
cout<< sizeof(b)<<endl; // 12bytes
cout<< sizeof(c)<<endl; // 8bytes
很奇怪么?定义的三个结构体,只是换了一下结构体中定义的成员的先后顺序,怎么最终得到的结构体所占用的内存大小却不一样呢?很诡异么?好了,这就是我这里要总结的内存对齐概念了。
内存对齐
内存对齐的问题主要存在于理解struct和union等复合结构在内存中的分布。许多实际的计算机系统对基本类型数据在内存中存放的位置有限制,它们会要求这些数据的首地址的值是某个数k(通常它为4或8)的倍数,这就是所谓的内存对齐。这个值k在不同的CPU平台下,不同的编译器下表现也有所不同,现在我们涉及的主流的编译器是Microsoft的编译器和GCC。
对于我们这种做上层应用的程序员来说,真的是很少考虑内存对齐这个问题的,内存对齐对于上层程序员来说,是“透明的”。内存对齐,可以说是编译器做的工作,编译器为程序中的每个数据块安排在适当的内存位置上。很多时候,我们要写出效率更高的代码,此时我们就需要去了解这种内存对齐的概念,以及编译器在后面到底偷偷摸摸干了点什么。特别是对于C和C++程序员来说,理解和掌握内存对齐更是重要的。
为什么要有内存对齐呢?该占用多大的内存,那就开辟对应大小的内存就好了,好比上面的结构体,两个char类型和一个int类型,大小应该是6bytes才对啊,怎么又是8bytes,又是12bytes的啊?对于内存对齐,主要是为了提高程序的性能,数据结构,特别是栈,应该尽可能地在自然边界上对齐。原因在于,为了访问未对其的内存,处理器需要做两次内存访问;然而,对齐的内存访问仅仅需要一次内存访问。
在计算机中,字、双字和四字在自然边界上不需要在内存中对齐(对字、双字和四字来说,自然边界分别是偶数地址,可以被4整除的地址和可以被8整除的地址)。如果一个字或双字操作数跨越了4字节边界,或者一个四字操作数跨越了8字节边界,就被认为是未对齐的,从而需要两次总线周期来访问内存。一个字起始地址是奇数,但却没有跨越字边界,就被认为是对齐的,能够在一个总线周期中被访问。综上所述,内存对齐可以用一句话来概括——数据项只能存储在地址是数据项大小的整数倍的内存位置上。
我们再来看看一个简答的例子:
#include <stdio.h>
struct Test
{
char a;
int b;
int c;
char d;
};
int main()
{
struct Test structTest;
printf("&a=%p\n", &structTest.a);
printf("&b=%p\n", &structTest.b);
printf("&c=%p\n", &structTest.c);
printf("&d=%p\n", &structTest.d);
printf("sizeof(Test)=%d\n", sizeof(structTest));
return 0;
}
- 上一篇:没有了
- 下一篇:C++实现查壳程序代码实例





