C语言结构体-大小,对齐
C语言中的结构体(struct)的定义
在C语言中,最常用的数据结构就是结构体了,结构体也是其它数据结构(比如链表等)的基础,结构体的使用非常简单。
比如,定义一个结构体:
structTEST_STRUCT{char Name[20];int Age;char Sex;};
在使用TEST_STRUCT的时候也很方便,如下代码所示:
void TestStruct(){structTEST_STRUCT test; test.Age = 20; test.Sex = 'M';strcpy(test.Name,"ZhangSan");}
为什么要使用结构体呢?用单个变量来分别表示姓名,性别和年龄不行吗?其实是可以的,只是会比较麻烦一点,而且看起来不直观,而且结构体更重要的一点,具有面向对象的属性,虽然C语言中并不支持类这个概念,但是结构体确确实实具有了对象这个属性,比如TEST_STRUCT这个结构体,如果改名为Person,Person就比较容易理解了,有姓名,年龄,性别等。
既直观,又很容易扩充,比如以后程序需要,这个Person需要添加一个新的属性家庭地址,则只需要添加一项即可,如代码所示:
structPerson{char Name[20];int Age;char Sex;char Address[32];};void TestPerson(){structPerson test; test.Age = 20; test.Sex = 'M';strcpy(test.Name,"ZhangSan");strcpy(test.Address,"Beijing");}
大家会发现,我们在使用这个Person或者其他结构体的时候,在定义一个Person变量的时候,往往需要在前面再加上一个struct关键字,在实际开发中,为了方便,我们在对结构体进行定义的时候,一般都会使用下面的这种方式:
typedefstruct_Person{char Name[20];int Age;char Sex;char Address[32];}Person,*PPerson;void TestPerson(){ Person test; test.Age = 20; test.Sex = 'M';strcpy(test.Name,"ZhangSan");strcpy(test.Address,"Beijing");}void TestPPerson(){ PPerson p = (PPerson)malloc(sizeof(Person)); p->Age = 20;free(p);}
即使用typedef的方式来对结构体再次进行定义,就好像取了一个别名一样,原来在使用struct Person的都可以用Person来代替了,更加直观和方便,在上面的例子中,还定义了Person的指针的别名PPerson,因此要使用Person的指针的话,可以用
Person *p这种方式或者PPerson p这个方式。
上面的代码仅仅是最基本的示例。
结构体的大小
那么前面使用的结构体所占的空间有多大呢?
即sizeof(Person)是多少呢?
先看一个最简单的例子,如下所示:
typedefstruct_TEST_INT{int a;int b;}TEST_INT,*PTEST_INT;
那么sizeof(TEST_INT)是等于8的,因为TEST_INT有两个成员字段,一个a,一个b,一个int占用4个字节空间,所以整个结构体占用8个字节。
如果是下面这样的结构体呢?sizeof应该是多少呢?
typedefstruct_TEST_INT{ char c; //1个字节int a;//4个字节int b;//4个字节 }TEST_INT,*PTEST_INT;
此时计算sizeof(TEST_INT),其大小并不是1+4+4的值9,这是为什么呢?
这里涉及到关于结构体的三个问题,数据对齐,数据填充和打包。(data alignment, data structure padding, and packing)
为什么要对齐?对齐是为了让CPU执行的速度的更快,比如32位的CPU,在32位体系结构中,如果数据存储在四个连续字节中,并且第一个字节位于4字节边界上,则称数据对齐了。
数据填充:为了确保对齐,可能需要在结构元素之间或结构的最后一个元素之后插入一些填充。例如,在32位机器上,包含一个16位值后跟一个32位值的数据结构可以在16位值和32位值之间有16位的填充,以便在32位边界上对齐32位值。只有当结构成员后面跟有更大对齐要求的成员或在结构的末尾时才插入填充。
打包:不使用对齐方式,将结构体按照一定大小要求打包在一起,也不进行填充,比如一个16位的值和一个32位的值在一起,不用进行填充16位值,打包后还是只有48位值,不用填充到63位值,这样可以节省16位的内存空间,但是执行效率就会慢了。
在C语言中,VC编译器或者GCC编译器都会对结构体进行自动对齐和填充处理,默认是4字节对齐方式。下面的描述都是假设以四字节对齐方式来描述,其他的选项比如以2字节,8字节对齐方式情形类似。
所以,在前面的例子中,sizeof(TEST_INT)就是12,因为c后面填充了3个字节,同样我们看一下下面这个例子,应该是多少呢?
typedefstruct_TEST_INT{ char c; //1个字节int a;//4个字节int b;//4个字节 char d[7];//7个字节}TEST_INT,*PTEST_INT;
d[7]占用7个字节,也需要按照4字节对齐方式进行填充,所以会占用8个字节,所以sizeof(TEST_INT)此时就是20.
记住前面提到的这一句话:
只有当结构成员后面跟有更大对齐要求的成员或在结构的末尾时才插入填充。
前面的两个例子都很好地满足了这句话或者说这个规则,我们再看一下例子,以加深理解。
typedefstruct_TEST_INT{ char c; //1个字节char d;//1个字节int a;//4个字节int b;//4个字节 }TEST_INT,*PTEST_INT;
由于c后面跟的是d,而d和c是一样的,都是一个字节类型,因此c后面补会填充,但是d后面跟的是a,a是4字节类型,因此d后面要填充2字节,因此siezof(TEST_INT)的大小是12.