内存对齐问题
首先我们拿几个问题热热身,如下所示的结构体,请诸位来判断下sizeof后的值的大小。
1.什么都没有的结构体(类),C++中这个结构体是多少?
struct A
{
};
2.那么在C语言中呢?上述的结构体是多少?
如果上面的例子让你拿捏不准的话,下面的例子给你一些自信:
3.
struct A
{
char a;
short b;
int c;
};
Sizeof(A)的值是多少?
如果你答的是7的话,可以回去等(xi)通(xi)知(shui)了(le)。
显然正确的结果基础扎实的同学是知道了,我们就以此为突破口来对内存对齐的知识进行剖析。
为什么要进行内存对齐?
回答7的同学一定很疑惑,1+2+4=7难道有错么,从我们直观的感受来说确实是正确的。但是计算机好像并不是以此为原则的,呵呵,结果说明一切。那么显然它有自己的一套规则。
许多计算机系统对基本数据类型合法地址做出了一些限制,要求某种类型对象的地址必须是某个值K(通常是2、4或8)的倍数。这种对齐限制简化了形成处理器和存储器系统之间接口的硬件设计。无论数据是否对齐,其实程序都能跑下去。但是Intel还是要建议对齐数据以提高存储器的性能。
上面的扯了那么多无非就是说,内存对齐是为了效率。那么它的对齐原则是什么,我们列举出来一一进行验证:
1.对于结构体的各个成员,第一个成员位于偏移量为0的位置,以后每个数据成员的偏移量必须是MIN(#pragma pack(x), 这个数据成员的自身长度)的倍数。
2.在数据成员完成各自对齐之后,结构体本身也要进行对齐,对齐将按照MIN(#pragma pack(x), 结构体(共用体)最大数据成员长度)。
这个#pragma pack(x)是什么意思?
指定成员按x个字节对齐。
#pragma pack(n) 编译器将按照n个字节对齐
#pragma pack() 编译器将取消自定义字节对齐方式
但是正如刚在的公式所说,每个成员还得按照自己的方式对齐。配合上边的#pragma:每个成员按其类型的对齐参数(通常是这个类型的大小)和指定对齐参数(这里是n字节)中较小的一个对齐。
例如
#pragma pack(8)
struct A
{
char a;
long b;
};
struct B
{
char c;
A d;
long long e;
};
A和B元素的所占字节的大小?
3.有得时候只是位置的调整就可以让他所占空间发生变化。
struct test
{
char a;
short b;
char c;
int d;
};
struct test2
{
short b;
char a;
char c;
int d;
};
上述的两个结构体大小分别为多少?
4.万事万物都有特例,回到第一个问题上。一个“空的结构体”有多大?
在C++中,一个空的struct占1字节,虽然struct没有任何元素,但是C++把struct当做类去看待。空类的默认大小为1。
而在C语言中,一个空的struct确实被当做一个空的结构体,那么它的大小也就是0了(在linux的gcc下编译)。
5.但是还有一些比较怪异结构体我们没有见过:
struct C
{
struct C a;
};
这个结构体的元素时结构本身。。。。。。所以大小是多少?
如果概念上大家已经明确的话,请接招(由简到难)。
1.
union A
{
int a;
double b;
int c;
char d;
};
Sizeof(A)?
2.
union A
{
int a;
double b;
int c;
char d;
};
struct B
{
A a;
char b;
};
Sizeof(B) ?
3.
union A
{
int a;
double b;
int c;
char d;
};
struct B
{
A a;
char b;
};
struct C
{
int i_value;
B b;
char ch;
};
Sizeof(C)?
4.
struct A
{
char a;
short b;
int c;
struct B
{
char d;
double e;
};
int f;
};
这个的结构体的大小是多少?(你可能会整错!)
5.
如果上边的算错了别灰心,看看下边这个。
struct A
{
char a;
short b;
int c;
struct B
{
char d;
double e;
} f;
int g;
};
这个和上边的非常相似,但是有区别?
4、5例子的对比将会引入一个新的概念。
6.
好吧,已经遇到了不少的陷阱了。那么再看看下边的。我们将会在结构体中添加位段(关于位段的概念参考《C和指针》)。
(1)
struct B
{
char c1:6;
char c2:1;
int in:16;
};
(2)
struct B
{
short s1:6;
char c2:1;
int in:16;
};
(3)
struct B
{
short s1:6;
char c2:1;
short s2:15;
int in:16;
};
(4)
热身完毕后得来点干货了!
struct B
{
short s1:6;
char c2:1;
short s2:15;
int in:16;
};
struct A
{
char a;
short b;
int c;
struct B e;
int g;
};
Sizeof(A)等于多少?
(5)
union B
{
short s1:6;
char c2:1;
short s2:15;
int in:16;
};
struct A
{
char a;
short b;
int c;
union B e;
int g;
};
(6)
union B
{
short s1:6;
char c2:1;
short s2:15;
int in:16;
double d:32;
};
这个的是多少?(算对说明思路清晰的很)。
(7)
struct A
{
char a;
short b;
int c;
union B
{
int b;
char c1:3;
short s2:12;
struct
{
int a;
double e;
};
};
int g;
};
长度很多,请说出sizeof(A)大小?
关于位段的考察告一段落,位段对齐原则的细节请自行在网上查阅。我们再转战到enum(枚举)。看看enum在结构体中的应用。
7.
struct A
{
enum a{
INDEX1,
INDEX2,
INDEX3,
INDEX4
};
};
这个的sizeof(A)是多少?
8.
struct A
{
int a;
short b;
int c;
double d;
struct B
{
int a;
enum c;
} e;
double f;
};
Sizeof(A)多少?
9.我们来看看数组问题。
struct A
{
char c[13];
int b;
double e[2];
char d[5];
};
总结:
至此我们已经考察了内存对齐的方方面面,对它的使用原则进行了简单的介绍,而且给出了很多变态的问题(不少的难度远超于面试难度)。那么引申出一个问题:
为什么企业面试这么看重内存对齐的概念?
1.在网络的数据传输中,如果能够组织起高效的数据报可以大大节省空间。
2.TCP和UDP的数据报,很多的位段用来描述数据报的状态。
3.考察学生是否进行过数据报的设计。
那么内存对齐的好处确实不少:
1.加快内存的读取速度,跳了效率;
2.确保程序的正常执行,有的存储器访问要求存储器地址必须是16的倍数,不满足的会导致程序终止(这个时候需要强制对齐)。
延伸阅读,请看看linux 和windows的对齐策略有什么不同?
参考《深入理解计算机系统》第三章:程序机器级表示。171页。
关于这篇博客博客上各sizeof值是多少,欢迎大家讨论。过段时间我将给出正确答案。
linux_player_c
西部开源技术中心
597114476@qq.com