结构体
结构就是一种把一些数据项组合在一起的数据结构。进行组合的通常方法就是把需要组合的东西放在花括号里面:{内容...}。关键字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属于相同的数据类型。结构中也允许存在位段,无名字段以及字对齐所需的填充字段。这些都是通过在字段的声明后面加一个冒号以及一个表示字段位长的整数来实现的。
struct pid_tag{
unsigned int inactive : 1;
unsigned int : 1; //一个位的填充
unsigned int refcount : 6;
unsigned int : 0; //填充到下一个字边界
short pid_id;
struct pid_tag *link;
};
位段的类型必须是int,unsigned int或signed int。至于int位段的值可不可取负值则取决于编译器。
两个跟结构有关的参数传递问题。参数在传递时首先尽可能地存放到寄存器中。注意,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;
}
int main(){
int i;
for(i=0;i<100;i++)lime.a[i]=1;
lemon=twofold(lime);
orange=lemon;//给整个结构体赋值
}
联合
联合的外表与结构相似,但在内存布局上存在关键性区别。在结构中,每个成员依次存储,而在联合中,所有的成员都从偏移地址0 开始存储。这样,每个成员的位置都重叠在一起:在某一时刻,只有一个成员存储于该地址。
联合的优点是它的外观同结构一样,只是用关键字union取代了struct。联合的一般形式如下:
联合一般是作为大型结构的一部分存在的。在有些大型结构中,存在一些与实际表示的数据类型有关的隐式或显式地信息。如果存储数据时是一种类型,但在提取该数据时却成了另外一种类型,这显然存在着类型的不安全性。
联合一般被用来节省空间,因为有些数据项时不可能同时出现的,如果同时存储它们,显然颇为浪费。例如,如果要存储一些关于动物种类的信息,首先想到的方法可能是:
struct creature{
char has_backbone;
char has_fur;
short num_of_legs_in_excess_of_4;
};
所有的动物要么是脊椎动物,要么是无脊椎动物。进而,只有脊椎动物才可能有皮毛,只有无脊椎动物才可能有多于4条的腿。没有一种动物既有毛皮又有超过4条的腿。因此可以把两个相互排斥的字段存储于一个联合中来节省空间:
union secondary_characteristics{
char has_fur;
short num_of_legs_in_excess_of_4;
};
struct creature{
char has_backbone;
union secondary_characteristics form;
};
这种方式可以节省备用的存储空间。如果有一个数据文件,里面存储了20000000个动物,使用这种方法,可以节省大约20MB的磁盘空间。
联合也可以把一个数据解释成两种不同的东西,而不是把两个不同的数据解释成同一种东西。
该用法的例子:
union bits32_tag{
int whole; //一个32位的值
struct { char c0,c1,c2,c3;}byte; //4个8位的字节
}value;
这个联合允许提取整个32位值(作为int),也可以提取单独的字节字段加value.byte.c0等。采用其他的方法也能达到这个目的,但联合不需要额外的赋值或强制类型转换。
枚举
枚举通过一种简单的途径,把一串名字与一串整型值联系在一起。对于像C这样的弱类型语言而言,很少有什么事只能靠枚举来完成而用#define不能解决的。所以,在大多数早期的C编译器中,都省掉了枚举。但是枚举在其他大多数语言中都存在,所以C语言最终也实现了它。现在对于枚举的一般形式:
enum 可选标签{ 内容... }可选变量定义;
其中的“内容...”是一些标识符的列表,可能有一些整型值赋给它们。下面是一个枚举实例:
enum sizes { small=7,medium,large=10,humungous };
默认情况下,整型值从0开始。如果对列表中的某个标识符进行了赋值,那么紧接其后的那个标识符的值就比所赋的值大1,然后类推。枚举有一个优点:#define定义的名字一般在编译时被丢弃,而枚举名字则通常一直在调试器中可见,可以在调试代码时使用它们。