结构体的内存分配机制
首先,结构在C语言中也是一种数据类型,叫做聚组类型(还包括数组)。他和其他的数据类型是一样的,在定义一个结构体的时候,系统并不会为他真正的分配内存空间(定义的结构体变量要在编译的阶段才分配空间,而结构体指针要显示的使用malloca来分配空间),也就是说,在定义结构体这种数据类型的时候是不会分配内存空间的,只有在定义变量的时候,才会分配。
下面是摘自百度百科 对结构题存储的三点:
1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
2) 结构体每个成员相对于结构体首地址的偏移量都是成员大小的整数倍,如有需要编译器会 在成员之间加上填充字节;
3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节
下面是来自《c和指针》的介绍
struct s1{
char a;
int b;
char c;
};
首先,结构体在存储的时候,结构体的首地址必须能够被其中最宽数据类型整除。(在s1中,最宽数据类型为int,在32位系统中为4Byte),
其次,参照第二条,第一个数据时char(已经保证了,结构的起始地址是4的整数倍),存储一个char,占一个Byte,要保证下一个int的存储起始地址是4的整数倍,所以要在char后面填充三个Byte,然后在存储第三个数据。
第三,最后一个也是char类型,他就占一个Byte,肯定是他存储位置的整数倍,最后参照第三条,结构体的总大小为最宽数据类型的整数倍,所以会在第二个char之后再填充三个Byte。
这样的话,总共占据的空间是1+3+4+1+3 = 12(红色为填充字符)
【但是】,调换一下结构中数据成员的顺序
struct s1{
int b;
char a;
char c;
};
同样地分析4+1+1+2 = 8
相比之下,存储空间的效率提高33%。
以下是系统对结构体的内存分配的详细介绍:
1、结构体变量的首地址是结构体中最宽数据类型的整数倍。
编译器在给结构体开辟空间时,首先找到结构体中最宽的基本数据类型,然后寻找内存地址能是该基本数据类型的整倍的位置,作为结构体的首地址。
2、结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节。
为结构体的一个成员开辟空间之前,编译器首先检查预开辟空间的首地址相对于结构体首地址的偏移是否是本成员的整数倍,若是,则存放本成员,反之,则在本成员和上一个成员之间填充一定的字节,以达到整数倍的要求,也就是将预开辟空间的首地址后移几个字节。
3、结构体的总大小为结构体中最宽基本数据成员的整数倍。如有需要,编译器将会在结构体的添加填充字符。
4、结构体中的数据成员按照所占空间从大到小的顺序来排列的话,这样存储空间就会得以提高。
4、现在对四条进行修正。在组织数据结构的数据成员的时候,可以将相同类型的成员放在一起,这样就减少了编译器为了对齐而添加的填充字符。
【提示】:
有些时候,我们不应该对结构的数据成员进行重排以减少因对齐带来的空间损失。因为在我们对数据成员重拍的时候,往往破坏了数据间的关联性,从而降低了程序的可读性和可维护性。
综上,我们可以得出,在对结构的成员进行重排的时候,即要考虑存储空间的使用效率,又要考虑程序的可读性与可维护性。
下面的代码是对上述几条的验证:
#include<stdio.h>
#include<stddef.h>
struct s1{
int a;
char b;
char c;
};
struct s2 {
char b;
char c;
int a;
};
int main(){
/*
struct s1 s;
s.a = 12;
s.b = 'a';
s.c = 'b';
printf("int s.a size: %d \n",offsetof(struct s1,a));
printf("char s.b size: %d \n",offsetof(struct s1,b));
printf("char s.b size: %d \n",offsetof(struct s1,c));
printf("size of struct s1: %d\n",sizeof(struct s1));
*/
struct s2 s;
s.a = 12;
s.b = 'a';
s.c = 'b';
printf("int s.a size: %d \n",offsetof(struct s2,a));
printf("char s.b size: %d \n",offsetof(struct s2,b));
printf("char s.b size: %d \n",offsetof(struct s2,c));
printf("size of struct s1: %d\n",sizeof(struct s2));
}
1 #include<stdio.h> 2 #include<stddef.h> 3 struct s1{ 4 int a; 5 char b; 6 char c; 7 }; 8 9 struct s2 { 10 char b; 11 char c; 12 int a; 13 14 }; 15 int main(){ 16 /* 17 struct s1 s; 18 s.a = 12; 19 s.b = 'a'; 20 s.c = 'b'; 21 22 printf("int s.a size: %d \n",offsetof(struct s1,a)); 23 printf("char s.b size: %d \n",offsetof(struct s1,b)); 24 printf("char s.b size: %d \n",offsetof(struct s1,c)); 25 printf("size of struct s1: %d\n",sizeof(struct s1)); 26 */ 27 28 29 struct s2 s; 30 s.a = 12; 31 s.b = 'a'; 32 s.c = 'b'; 33 34 printf("int s.a size: %d \n",offsetof(struct s2,a)); 35 printf("char s.b size: %d \n",offsetof(struct s2,b)); 36 printf("char s.b size: %d \n",offsetof(struct s2,c)); 37 printf("size of struct s1: %d\n",sizeof(struct s2)); 38 }