这一类型的题目经常在笔试、面试中问到,而很多人虽然答对了,但却了解的不够彻底,所以在此总结下。有错请帮忙指出。
对齐的原因不多讲了,相信大家都知道。这里只谈它的机制。
在c里,每种数据类型都有自己的对齐方式,包括基本数据类型以及其他的复杂数据类型。对于标准数据类型,它的地址只要是它的长度的整数倍就行了,而非基本数据类型按下面的原则对齐:
数组:按照基本数据类型对齐,第一个对齐了后面的自然也就对齐了。
联合:按其包含的长度最大的数据类型对齐。
结构体:结构体中每个数据类型都要对齐,结构体本身也要对齐。
这里主要讨论结构体的对齐方式。(vs 2012下,编译器默认对齐是8字节)
考虑这样一个结构体:
struct A { float f; char c; short s; int i; }
它的大小是多少呢?答案是12。要搞清楚这一点,需要了解几个概念:
1. 数据类型的自身对齐大小。比如float,它是4字节的,它自身的对齐大小也是4字节。
2. 编译器指定的对齐大小。 可以通过#pragma pack (N)来指定,vs下默认是8。
3. 有效对齐大小。前两者的最小值。有效对齐大小是最终采用的对齐大小,如果其值为N,则该数据的首地址能被N整除。
4. 结构体本身的有效对齐大小是其数据成员有效对齐大小的最大值。
在上题中,float自身对齐大小是4字节,而默认对齐大小是8,所以有效对齐大小是4。float是结构体中最大的数据类型,所以结构体自身的有效对齐大小是4,结构体的首地址能被4整除,比如说0x0004.float的首地址与之相同,所以是自然对齐的。数据c自身对齐大小是1,小于指定大小,所以其有效对齐大小是1,由于结构体是顺序连续摆放的,所以其地址是0x0008。s的有效对齐大小是2,其地址要能被2整除,所以他应该放在0x0010,而数据i有效对齐大小是4,应该在0x000C。所以总共是12。
如果其中有数组怎么办呢?
struct A { float f; char c; int i; char a[10]; };
上面说过了,数组按照第一个元素对齐。所以这个结构体的大小是4+1+3+4+10=22,还要加上2,结构体的大小要能被其有效对齐大小整除,这里也就是f或者i的有效对齐大小4,所以最终结果是24。
#pragma pack (16) struct A { float f; char c; int i; double a[10]; };
这个结构体的大小呢?答案是96。你明白了吗?