关于C语言结构体
一:结构体基本特性
首先看struct关键字。struct与其后的{...}共同构成了一种与int,double等类似的“模型”,暗示了一种编译器对待数据类型的方法,因此struct {...}后面可以跟一个变量表。不同的地方在于struct 和花括号之间可以插入一个标记,让用户可以在程序的一个地方声明这一模型,然后在程序的其他位置一次或多次地运用它。
再看空间分配问题和初始化。首先,在函数内声明结构体变量时,我们声明的是一个局部变量,当函数结束时,这个变量的生命周期也随之结束。因此函数想要返回结构体时,务必使用指向结构体的指针。其次,这里的一篇文章提到空的结构体也会占用内存,然而我测试后发现空的结构体并不占内存,测试结果如下。然后,应该注意的是结构体占用的内存中可能存在空隙,也就是说结构体占用的内存并非结构体中各数据类型理论占用的内存之和。此处需单独讨论。
1.空结构体不占内存 2.结构体占用内存可能出现空隙
最后,C99标准提出了所谓“柔性数组”的说法,即结构体的最后一个成员可以是一个未声明长度的数组,可以利用malloc函数分配内存,这个数组所占的内存不属于结构体。至于初始化,推荐使用linux内核使用的初始化方式,即
Test test = { .a = 0, .b = 0, .c = 0 }
二:位字段
C语言中的结构还有一种不常用的使用方法,即位字段,在K&R C一书上有如下示例:
struct { unsigned int is_keyword: 1; unsigned int is_static: 3; unsigned int is_extern: 1; } flags;
位字段是在资源紧张的情况下的一种存储数据的替代手段,可以在一个字的特定位上互不干扰地存储数据。根据笔者的观察,在Mac OS下位字段是从左到右分配的,有点类似于“小端法”的存储方式。内存占用单独讨论。
下面是另外的一些关于位字段的规则:(来自这里)
1. 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;
2.如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;
3.如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式(不同位域字段存放在不同的位域类型字节中),Dev-C++和GCC都采取压缩方式。
三:内存对齐和总长度填充
内存对齐即指在内存中存放数据时,以字长的整数倍作为存储数据的起点,但笔者认为结合上文关于位字段的知识,内存对齐也表现在一个数据在存储时会尽可能的避开字的交界处。这样做的好处在于使CPU更高效的读写内存。
结构体进行的总长度填充也是为了类似的目的,但此处笔者尚未搞清楚填充的规则,因此暂不讨论,下次更新。