struct是一种复合数据类型,其构成元素既可以是基本数据类型(如int、long、float等)的变量,也可以是一些复合数据类型(如array、struct、union等)的数据单元。对于结构体,编译器会自动进行成员变量的对齐,以提高运算效率。缺省情况下,编译器为结构体的每个成员按其自然对齐(natural alignment)条件分配空间。各个成员按照它们被声明的顺序在内存中顺序存储,第一个成员的地址和整个结构的地址相同。
自然对齐(natural alignment)即默认对齐方式,是指按结构体的成员中(类型)size最大的成员作为基本的分配单元,而且与其顺序有这密切的联系。
例如:
struct naturalalign
{
char a;
short b;
char c;
};
在上述结构体中,size最大的是short,其长度为2字节,因而结构体中的char成员a、c都以2为单位对齐,sizeof(naturalalign)的结果等于6;
对于C语言中结构体所占的存储空间的大小,也一直是笔试面试的常客,今天好好看了一下这方面,以前一直以为很清楚了,今天通过各种实际测试举例,发现原来还是没有搞透彻,好在现在是彻底懂了,所以和大家分享,希望能有所帮助。
int a;
charb[9];
charc;
};
charb[9];
inta;
charc;
};
charb[9];
inta;
charc;
doubled;
};
charb[9];
inta;
doubled;
charc;
};
doubled;
charb[9];
inta;
charc;
};
提到结构体,相信大家都知道元素存储要对齐,话是没有错,只是这个“对齐”里面包含了很多微妙的东西。首先详细的给出结构体内存分配的原则吧:编译器按照成员列表顺序一个接一个地给每个成员分配内存。只有当存储成员时需要满足正确的边界对齐要求时,成员之间才可能出现用于填充的额外内存空间。接下来我们来慢慢理解这句话。
sizeof操作符能够得出一个结构的整体长度,包括因边界对齐而跳过的那些字节。所以一般都用sizeof来计算其所占存储空间。来看下面的例子:
structtest1
{
计算可以得到test1占16个字节。原因是a占4个字节(32位机,int占4个字节),b和c分别占9个字节和1个字节,加起来10个字节,与a对齐,则需要12个字节,所以总共需要16个字节。这个例子就是我们最熟悉的,接下来看一下个。
structtest2
{
计算可以得到test2占20个字节。比较test1和test2可以发现仅仅将a和b的位置换了一下,为什么结果会不同呢?这就是前面定义中所说的顺序的问题。int为最大字节,一开始分配9个字节,然后是a,为了对齐,这时候应该是12+4个字节,后面又来一个char,为了对齐加4,所以总共是20个字节。而test1因为后面两个类型一致,所以可以和在一起分配,然后对齐。
接下来我们在加大难度,看如下例子:
structtest3
{
按照上面的逻辑,b占9个字节,a占4个字节,为了和a对齐,所以此时应该是12+4,接着是c为1个字节总共为12+4+1=17,然后是d为8个字节,为了和8字节对齐,则应该为24+8=32个字节,即最终结果为32个字节。变换一下顺序,得到如下例子:
structtest4
{
同样的,b和a占据12+4个字节,d为8个字节,则应该是16+8=24,加上最后的一个字节,为了对齐,总共也是24+8=32个字节。这里需要注意的是,若数组b元素改为13,则为了对齐,d前面应该是16+4,然后遇到d,对齐后为24+8,在加上最后的8个字节,应该为40个字节。
再来看一个:
structtest4
{
d占8个字节,b占9个字节,后面是int,为了对齐,加上a应该是20+4,再加上1个字节,为了和最大的double对齐,所以应该是20+4+8=32个字节。
好了,通过列举这么多不同的例子,最终目标只有一个,是真的搞清楚某个结构体占据的空间大小。通过上面的例子,我总结了以下几点。
1)首先,看该结构体中最大的“基元素”(这是我杜撰的,就是说基本元素类型最大的是哪个,例如test1和test2都是int,而test3和test4都是double,而不是看数组总共所占据的大小),找到这个以后,结构体所占据的空间的大小一定要是该基本元素所占空间大小的整数倍。
2)接着,按顺序将元素进行排列。前一元素要与后一元素对齐,如果后一元素的“基元素”比前一个大,则前一元素需要调整大小,即对齐该后一元素。
3)最后将得到的字节大小加起来,跟最大的基元素对齐,即得到最终的所占存储空间大小。
如果要确定结构中某个成员的实际位置,可以使用offsetof宏(stddef.h)offsetof(type,member)。type是结构的类型,member是该结构的成员名,表达式的结构是一个sizeof_t值。表示该指定成员的存数位置(离该结构体存储的起始位置)。
另外还有一点值得一提的是,当结构体里面包含另一结构体时,直接将该结构体中的内容代换进去,计算其总的存储空间即可。