C编程
在《变量》章节中,我们介绍了基本数据类型。然而,先进的数据类型使我们能够在程序中更灵活地管理数据。
结构体(Structs)
结构体是由其他数据类型的变量(可能包括其他结构体)组成的数据类型。它们用于将信息块组织成有意义的单元,并且允许一些在其他情况下无法实现的结构。结构体中的变量称为“成员”。定义结构体使用 struct
关键字。例如:
struct mystruct {
int int_member;
double double_member;
char string_member[25];
} struct_var;
struct_var
是类型为 struct mystruct
的变量,我们在定义新结构体 mystruct
数据类型时同时声明了它。更常见的做法是,在定义结构体之后声明结构体变量,形式如下:
struct mystruct struct_var;
通常的做法是创建类型别名,这样我们就不必每次都输入“struct mystruct
”。C 允许通过 typedef
语句为类型创建别名:
typedef struct {
// ...
} Mystruct;
结构体本身是一个不完整的类型(因为在第一行没有名称),但是它被别名为 Mystruct
。然后可以这样使用:
Mystruct struct_var;
可以使用成员访问运算符(.
)来访问结构体变量的成员,或者如果结构体变量是指针,则使用间接成员访问运算符(->
,箭头):
struct_var.int_member = 0;
struct_var->int_member = 0; // 此语句等价于:(*struct_var).int_member = 0;
(指针将在下一章讲解。)结构体不仅可以包含自己的变量,还可以包含指向其他结构体的变量。这使得递归定义成为可能,当与指针结合使用时非常强大:
struct restaurant_order {
char description[100];
double price;
struct restaurant_order *next_order;
};
这是链表数据结构的实现。每个节点(一个餐厅订单)指向另一个节点。链表在最后一个节点处终止(在我们的例子中,这是最后一个订单),其 next_order
变量会被赋值为 NULL
。
使用 typedef
时,递归结构体定义可能会变得复杂。由于在 typedef
语句被求值之前,别名定义并不存在,因此无法使用别名定义来声明结构体变量:
typedef struct Mystruct {
// ...
struct Mystruct *pointer; // Mystruct *pointer; 会导致编译错误
} Mystruct;
结构体类型的大小至少是所有成员大小的总和。但编译器可以在结构体成员之间插入填充字节,以将成员对齐到某些约束。例如,包含一个 char
和一个 float
的结构体,在许多 32 位架构中将占用 8 字节。
联合体(Unions)
联合体的定义类似于结构体。两者的区别在于,结构体的成员占用不同的内存区域,而联合体的成员占用相同的内存区域。因此,在下面的类型定义中:
union {
int i;
double d;
} u;
程序员可以访问 u.i
或 u.d
,但不能同时访问这两个成员。由于 u.i
和 u.d
占用相同的内存区域,修改其中一个会修改另一个的值,有时会以不可预测的方式发生。这也是联合体在实践中很少使用的主要原因。
联合体的大小是其最大成员的大小。
枚举(Enumerations)
枚举是人工数据类型,用于表示标签和整数之间的关联。与结构体或联合体不同,枚举并不是由其他数据类型组成的。以下是一个示例声明:
enum color {
red,
orange,
yellow,
green,
cyan,
blue,
purple,
} crayon_color;
在上面的例子中,red
等于 0,orange
等于 1,依此类推。可以为标签分配整数值,但它们必须是常量。
类似于结构体和联合体的声明语法,同样适用于枚举。此外,通常不需要关心标签所代表的整数值:
enum weather weather_outside = rain;
这一特殊性质使得枚举在 switch-case
语句中尤为方便:
enum weather {
sunny,
windy,
cloudy,
rain,
} weather_outside;
// ...
switch (weather_outside) {
case sunny:
wear_sunglasses();
break;
case windy:
wear_windbreaker();
break;
case cloudy:
get_umbrella();
break;
case rain:
get_umbrella();
wear_raincoat();
break;
}
枚举是 C 中模拟关联数组的一种简化方式。