C语言聚合数据类型包括数组和结构体,其中数组中是相同类型的元素的集合,可以通过下标引用或之子很间接访问,但结构体各个成员可以是不同的数据类型。
结构声明
完整的结构声明:struct tag {number-list} variable-list; 黑体部分至少要出现两个。
{number-list}和variable-list,每次声明新变量都要用单独的声明,而且每次声明后并不是同一种类型,尽管结构体内容完全一样。
1 struct { 2 int a; 3 float b; 4 char c; 5 }x; 6 7 struct { 8 int a; 9 float b; 10 double c; 11 }*z; 12 13 z = &x;//这样赋值是错误的,因为类型不一致,但C语言会忽略,C++会报错
tag和{number-list},这样不用每次都使用单独的声明,只用struct tag即可对结构变量进行声明。
1 struct temp{ 2 int a; 3 float b; 4 char c; 5 }; 6 struct temp x; 7 struct temp *z;
typedef、{number-list}和name,这样不用每次都使用单独的声明,只用name即可对结构变量进行声明,比上面的tag更加简洁,这时name是类型名,而不是结构标签。
1 typedef struct { 2 int a; 3 float b; 4 double c; 5 }temp; 6 temp x; 7 temp *z;
结构体初始化
(1)定义时赋值,需要对应顺序,不能错位
1 temp x = {1, 2.3, 4.66};
(2)定义后逐个赋值
temp x; x.a = 1; x.b = 2.3; x.c = 4.66;
(3)定义时乱序赋值(C风格),这种方法在Linux内核(kernel)中经常使用,在音视频编解码库FFmpeg中也大量频繁使用,还是很不错的一种方式。
temp x = { .b = 2.3, .a = 1, .c = 4.66 };
(4)定义时乱序赋值(C++风格),类似于key-value键值对的方式
temp x = { b : 2.3, a : 1, c : 4.66 };
访问结构成员
(1)直接访问
如上面声明的结构体变量所示,对x中的成员进行访问时,如下访问
int m = x.a; float = x.b;
(2)间接访问
如果存在指向结构体的指针,则如下访问
int m = (*z).a; int m = z->a;
结构的自引用
结构内部包含一个类型为结构本身的成员,为自引用,高级的数据结构,链表和树都用到了该种结构。
声明时,编译器需知道结构体的大小,以便分配内存空间,所以不能直接用该结构体,否则会无限循环下去,无法知道结构体的大小。因此需要对该结构体成员使用指针。指针长度是固定的,取决于使用的机器和编译器,因此长度可知。
1 typedef struct { 2 int a; 3 temp *b; 4 double c; 5 }temp; 6 //上面是错误的 7 typedef struct selftemp_tag{ 8 int a; 9 struct selftemp_tag *b; 10 double c; 11 }selftemp;
上面的声明是错误的,因为temp *b在声明之前就使用了。下面的声明中,定义了一个tag标签来解决该问题,同时标签前需加struct。
结构的互引用(不完整声明,还不知道怎么用)
结构体之前互相引用,那么哪个结构应该首先声明呢?问题的解决方案是使用不完整声明,它声明一个作为结构标签的标识符
1 struct B; 2 3 struct A{ 4 struct B * partner; 5 //其他声明 6 }; 7 8 struct B{ 9 struct A * partner; 10 //其他声明 11 };
在A的成员列表中需要标签B的不完整声明,一旦A声明之后,B的成员列表也可被声明.(并不知道怎么用)
结构进行函数传参
结构变量用作函数参数进行传递时,如果传递整个结构体的话,结构体成员少时并没有影响,若成员很多,传递整个结构体会浪费内存空间,因此这时应该传递结构体指针,从而提高传递效率。
联合
如果想在不同时刻把不同的东西存储在同一个位置,就需要用到联合,声明和结构体类似
union { float f; int i; }fi;
当存储在同一位置的不仅仅是简单的整型或浮点型数值,而是完整的结构时,称为变体记录,具体程序实例参考《c和指针》P213
联合的各个成员具有不同的长度,联合的长度就是它最长成员的长度,因此如果没有动态内存分配的话,默认按照最长成员的长度进行内存分配,会造成内存空间的浪费。
联合的变量可以被初始化,但初始值必须是联合第一个成员的类型。
union { float f; int i; }fi = {0.5};
初始化的值必须位于一对花括号里面。