VectorLu

结构、联合和枚举

函数延迟绑定;数据结构导致绑定。
记住:在编程过程后期再结构化数据。

声明时:structunion 的每个成员用分号隔开,enum 的每个成员用逗号隔开。

结构变量

每个结构都为它的成员设置了独立的 name space。

将点号和成员名称的组合称为指示符,可用指示符进行指定初始化 (C99):

1
2
3
4
5
struct {
int number;
char name[NAME_LEN+1];
int on, hand;
} part1 = {.number = 528, .name = "Disk drive", .on_hand = 10};

句点运算符的优先级和后缀 ++-- 一样,所以优先级几乎高于其他所有运算符。

结构可以用 = 运算符进行复制(仅用于类型兼容的结构),而且对结构复制时,嵌在结构里的数组也得到了复制。可以利用这种性质来产生“空结构”,已封装稍候将进行复制的数组:

1
2
struct { int a[10]; } a1, a2;
a1 = a2;

但是除了赋值运算,C 语言没有提供其他用于整个结构的操作。特别是不能使用运算符 ==!= 来判定两个结构相等还是不等。

联合

用联合来节省空间

假设礼品册上只有三种商品:书籍、被子和衬衫,每种商品都含有库存量、价格一级与商品类型相关的其他信息:

  • 书籍:书名、作者、页数。
  • 杯子:设计。
  • 衬衫:设计、可选颜色、可选尺寸。

最初的设计可能会得到如下结构:

1
2
3
4
5
6
7
8
9
10
11
struct catalog_item{
int stock_number;
float price;
int item_type;
char title[TITLE_LEN+1];
char author[AUTHOR_LEN+1];
int num_pages;
char design[DESIGN_LEN+1];
int colors;
int sizes;
};

虽然上述结构十分好用,但是很浪费空间,因为只有结构中的部分信息是常用的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct catalog_item{
int stock_number;
float price;
int item_type;
union{
struct{
char title[TITLE_LEN+1];
char author[AUTHOR_LEN+1];
int num_pages;
} book;
struct{
char design[DESIGN_LEN+1];
} mug;
struct{
char design[DESIGN_LEN+1];
int colors;
int sizes;
} shirt;
} item;
};

如果 c 是表示书籍的结构 catalog_item,那么刻意用下列方法显示书籍的名称:

1
printf("%s", c.item.book.title);

用联合来构造混合的数据结构

假设需要数组的元素是 int 值和 double 值的混合,因为数组的元素必须是相同的类型,所以要利用联合。

1
2
3
4
5
6
7
8
9
typedef union{
int i;
double d;
} Number;
Number number_array[1000];
number_array[0].i = 5;
number_array[1].f = 8.295;

为联合添加“标记字段”

如果:

1
2
3
4
5
6
void print_number(Number n)
{
if (n 包含一个整数)
{printf("%d", n.i);}
else {printf("%g", n.d);}
}

但是,没有办法可以帮助函数确定 n 包含到是整数还是浮点数。

为了记录此信息,可以把联合嵌入一个结构中,且此结构还含有另一个成员:“标记字段”,它是用来提示当前存储在联合中的内容的。

比如之前讨论的结构 catalog_item 中的 item_type 就是用于该目的。

1
2
3
4
5
6
7
8
9
10
#define INT_KIND 0
#define DOUBLE_KIND 1
typedef struct{
int kind; // tag field
union{
int i;
double d;
} u;
} Number;

Number 有两个成员 kindukind 的值可能是 INT_KINDDOUBLE_KIND

每次给 u 的成员赋值时,也会改变 kind,从而提示修改的是 u 的哪个成员。

1
2
3
4
5
6
7
8
9
10
11
Number n;
n.kind = INT_KIND;
n.u.i = 82;
void print_number(Number n)
{
if (n.kind == INT_KIND)
{printf("%d", n.u.i);}
else
{printf("%g", n.u.d);}
}

枚举

枚举是整数,值(默认)从 0 开始逐渐增加,所以是很理想的下标。
在 C89 中,利用 typedef 来命名枚举是创建布尔类型的一种非常好的方法:

1
2
3
4
typedef enum {FALSE, TRUE} Bool;
enum suit {CLUSBS, DIAMONDS, HEARTS, SPADES};
typedef enum {CLUBS, DIAMONDS, HEARTS, SPADES) Suit;

枚举作为整数

1
enum suit {CLUBS = 1, DIAMONDS = 2, HEARTS = 3, SPADES = 4};

允许与普通整数混合使用。

用枚举声明“标记字段”

1
2
3
4
5
6
7
typedef struct{
enum (INT_KIND, DOUBLE_KIND) kind;
union {
int i;
double d;
} u;
} Number;
您的支持将鼓励我继续创作!

热评文章