结构体就是一种把一些数据组合在一起的数据结构。在C语言中,进行组合的通常方法是把需要组合的东西放在花括号里面:{内容…}。关键字struct放在左花括号前面,以便编译器能够从程序块中认出它:struct {内容…}

结构体的内容可以是任何其他数据声明:单个数据项、数组、其他结构、指针等。我们可以在结构体的定义后面跟一些变量名,表示这些变量的类型是这个结构。
例如:

1
struct {内容...} plum, pomegranate, pear;

另外还需要注意的一点是,可以在struct关键字后面加一个可选的”结构标签“:

1
struct fruit_tag {内容...} plum, pomegranate, pear;

这样,我们就可以在将来的声明中用struct fruit_tag作为struct {内容…} 的简写形式了。
因此,结构体的通常形式是:

1
2
3
4
5
6
struct 结构标签(可选){
类型1 标识符1;
类型2 标识符2;
...
类型N 标识符N;
}变量定义(可选);

结构体中也允许存在位段、无名字段以及字对齐所需的填充字段。这些都是通过在字段的声明后面加一个冒号以及一个表示字段位长的整数来实现的。

1
2
3
4
5
6
7
8
9
/* 处理ID信息 */
struct pid_tag {
unsigned int inactive : 1;
unsigned int : 1; //1个位的填充
unsigned int recount : 6;
unsigned int : 0; //填充到下一个字边界
short pid_id;
struct pid_tag *link;
}

这种用法通常被称作”深入逻辑元件的编程“,你可以在系统编程中看到它们。它也能用于把一个布尔标志以位而不是字符来表示。位段的类型必须是int, unsigned int 或 signed int (或加上限定符)。至于int位段的值可不可以取负值则取决于编译器。

我不喜欢把结构体的声明和变量的定义混合在一起,更喜欢采用:

1
2
struct veg {int weight ,price_per_lb};
struct veg onion,radish,turnip;

而不是:

1
struct veg {int weight ,price_per_lb}onion,radish,turnip;

确实,后面一种方法可以少打了几个字,但我们应该更关心代码是否容易阅读,而不是是否容易书写。我们只编写一次代码,但在以后的程序维护过程中将多次阅读这些代码。如果一行代码只做一件事,看上去会简单些。基于这个理由,变量的声明应该与类型的声明分开。

两个跟结构体有关的参数传递问题:
1.有些C语言书籍声称”在调用函数时,参数按照从右到左的次序压倒堆栈里。“这种说法过于简单了。参数在传递时首先尽可能的存放到寄存器中(追求速度)。注意,int型变量i跟只包含一个int型成员的结构体变量s在参数传递时的方式可能完全不同。一个int型参数一般会被传递到寄存器中,而结构体参数则很可能被传递到堆栈中。

2.在结构体中放置数组,如:

1
struct s_tag { int a[100]; };

现在,你可以把数组当作第一等级的类型,用赋值语句拷贝整个数组,以传值调用的方式把它传递到函数,或者把它作为函数的返回类型。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <stdio.h>
struct s_tag {int a[10];};
struct s_tag orange,lime,lemon;
struct s_tag twofold(struct s_tag s){
int j;
for(j=0;j<10;j++){
s.a[j] *= 2;
}
return s;
}
int main(void){
int i;
for(i=0;i<10;i++){
lime.a[i] = 1;
}
lemon = twofold(lime);
orange = lemon;
for(i=0;i<10;i++){
printf("%d ",lime.a[i]);
}
printf("\n");
for(i=0;i<10;i++){
printf("%d ",lemon.a[i]);
}
printf("\n");
for(i=0;i<10;i++){
printf("%d ",orange.a[i]);
}
printf("\n");
return 0;
}

程序运行输出:

1
2
3
1 1 1 1 1 1 1 1 1 1 
2 2 2 2 2 2 2 2 2 2
2 2 2 2 2 2 2 2 2 2

在典型情况下,并不会频繁地对整个数组进行赋值操作。但是如果需要这样做,可以通过把它放入到结构体中来实现。

在结构体中包含指向结构本身的指针:
在结构体中包含一个指向结构本身的指针,这种方法常用于列表(list)、树(tree)以及许多其他动态数据结构。

1
2
3
4
5
6
7
8
/*结构体中有一个指向结构本身的指针*/
struct node_tag {
int datum;
struct node_tag *next;
};
struct node_tag a, b;
a.next = &b; /* a, b链接在一起 */
a.next->next = NULL;