结构体就是一种把一些数据项组合在一起的数据结构。其他编程语言把它称为记录。结构体的语法很容易记忆:在C语言中,进行组合的通常方法就是把需要组合的东西放在花括号里面:{内容……}。关键字struct放在左括号前面,以便编译器能够从程序块中认出它: struct {内容……}
结构体内容可以是任何其他数据声明:单个数据项、数组、其他结构、指针等。我们可以在结构体的定义后面跟一些变量名,表示这些变量的类型是这个结构。例如:
struct {内容……} plum,pomegranate,pear;
另外还需要注意的一点是,可以在struct关键字后面加一个可选的“结构标签”:
struct fruit_tag {内容……} plum,pomegranate,pear;
这样,我们就可以在将来的声明中用struct fruit_tag作为struct {内容……}的简写形式了。
因此,结构体的通常形式是:
struct 结构标签(可选)
{
类型1 标识符1;
类型2 标识符2;
...
类型N 标识符N;
} 变量定义(可选);
所以,在下面的声明中:
struct date_tag { short dd, mm, yy; } my_birthday, xmas;
struct date_tag easter, groundhog_day;
变量my_birthday、xmas、easter和groundhog_day属于相同的数据类型。结构中也允许存在位段、无名字段以及字对齐所需的填充字段。这些都是通过在字段的声明后面加一个冒号以及一个表示字段位场的整数来实现的。
注:在32位机器上unsigned int位32位,所以下面的结构体总共占用10个字节。
注:位段不允许跨字节。
struct pid_tag
{
unsigned int inactive :1; //4字节中第一个字节的第0位
unsigned int :1; //4字节中第一字节的第1位。无名位段用于填充
unsigned int recount :6 //4字节中第一字节的第2位,长度6个bit
unsigned int :0 //4字节中剩余的3个字节使用0填充,表示不使用。“类型 :0 ”表示类型所表示的字节数的剩余部分不使用
short pid_id; //2字节
struct pid_tag *link; //4字节
}
这种用法通常被称作“深入逻辑元件的编程”,你可以在系统编程中看到它们。它也能用于把一个布尔标志以位而不是字符来表示。位段的类型必须是int、unsigned int或signed int(或加上限定符)。至于int位段的值可不可以取负值则取决于编译器。
我不喜欢把结构体的声明和变量的定义混合在一起。我更喜欢采用:
struct veg {int weight,price_per_lb;}
struct veg onion,radish,turnip;
而不是:
struct veg {int weight,price_per_lb;} onion,radish,turnip;
确实,后面一种方法可以少打几个字,但我们应该更关系代码是否容易阅读,而不是容易书写。我们只编写一次代码,但在以后的程序维护过程中将多次阅读这些代码。如果一行代码只做一件事,看上去会更简单一些。基于这个理由,变量的声明应该与类型的声明分开。
最后,还有两个跟结构体有关的参数传递。有些C语言书籍声称“在函数调用时,参数按照从右到左的次序压入堆栈里。”这种说法过于简单——如果你有一本这样的书,把那一页撕下烧掉。如果你有一个这样的编译器,把该编译器源代码的那几行删掉。参数在传递时首先尽可能地存放到寄存器中(追求速度)。注意,int变量i,与只包含一个int类型成员的结构体变量s在参数传递时的方式可能安全不同。一个int类型参数一般会被传递到寄存器中,而结构体则很可能被传递到堆栈中。第二点需要注意的是,在结构体中放置数组,如:
struct s_tag { int a[100];};
现在,你可以把数组当作第一等级的类型,用赋值语句拷贝整个数组,以传值调用的方式把它传递给函数,或者把它作为函数的返回类型。
struct s_tag {int a[100];};
struct s_tag orange,lime,lemon;
struct s_tag twofold(struct s_tag s)
{
int j;
for(j=0;j<100;j++)
s.a[j]*=2;
return s;
}
main()
{
int i;
for(i=0;i<100;i++)
lime.a[i]=1;
lemon=twofold(lime);
orange=lemon;
}
在典型情况下,并不会频繁地对整个数组进行赋值。但是如果需要这样,可以通过把它放入结构中来实现。让我们展示在结构中包含一个指向结构本身的指针,这种方法通常用于列表(list)、树(tree)以及许多其他动态数据结构。
struct node_tag
{
int datum;
struct node_tag *next;
};
struct node_tag a, b;
a.next = &b;
a.next->next = NULL;