3. 数据类型标志
在上一节中,我们通过一个复数存储表示抽象层把 complex_struct 结构体的存储格式和上层的复数运算函数隔开, complex_struct 结构体既可以采用直角座标也可以采用极座标存储。但有时候需要同时支持两种存储格式,比如先前已经采集了一些数据存在计算机中,有些数据是以极座标存储的,有些数据是以直角座标存储的,如果要把这些数据都存到 complex_struct 结构体中怎么办?一种办法是规定 complex_struct 结构体采用直角座标格式,直角座标的数据可以直接存入 complex_struct 结构体,而极座标的数据先转成直角座标再存,但由于浮点数的精度有限,转换总是会损失精度的。这里介绍另一种办法, complex_struct 结构体由一个数据类型标志和两个浮点数组成,如果数据类型标志为 0,那么两个浮点数就表示直角座标,如果数据类型标志为 1,那么两个浮点数就表示极座标。这样,直角座标和极座标的数据都可以适配(Adapt)到 complex_struct 结构体中,无需转换和损失精度:
enum coordinate_type { RECTANGULAR, POLAR };
struct complex_struct {
enum coordinate_type t;
double a, b;
};enum 关键字的作用和 struct 关键字类似,把 coordinate_type 这个标识符定义为一个 Tag, struct complex_struct 表示一个结构体类型,而 enum coordinate_type 表示一个枚举(Enumeration)类型。枚举类型的成员是常量,它们的值由编译器自动分配,例如定义了上面的枚举类型之后, RECTANGULAR 就表示常量 0, POLAR 表示常量 1。如果不希望从 0 开始分配,可以这样定义:
enum coordinate_type { RECTANGULAR = 1, POLAR };这样, RECTANGULAR 就表示常量 1,而 POLAR 表示常量 2。枚举常量也是一种整型,其值在编译时确定,因此也可以出现在常量表达式中,可以用于初始化全局变量或者作为 case 分支的判断条件。
有一点需要注意,虽然结构体的成员名和变量名不在同一命名空间中,但枚举的成员名却和变量名在同一命名空间中,所以会出现命名冲突。例如这样是不合法的:
int main(void) {
enum coordinate_type { RECTANGULAR = 1, POLAR };
int RECTANGULAR;
printf("%d %d\n", RECTANGULAR, POLAR);
return 0;
}complex_struct 结构体的格式变了,就需要修改复数存储表示层的函数,但只要保持函数接口不变就不会影响到上层函数。例如:
struct complex_struct make_from_real_img(double x, double y) {
struct complex_struct z;
z.t = RECTANGULAR;
z.a = x;
z.b = y;
return z;
}
struct complex_struct make_from_mag_ang(double r, double A) {
struct complex_struct z;
z.t = POLAR;
z.a = r;
z.b = A;
return z;
}习题
本节只给出了
make_from_real_img和make_from_mag_ang函数的实现,请读者自己实现real_part、img_part、magnitude、angle这些函数。编译运行下面这段程序:
c#include <stdio.h> enum coordinate_type { RECTANGULAR = 1, POLAR }; int main(void) { int RECTANGULAR; printf("%d %d\n", RECTANGULAR, POLAR); return 0; }结果是什么?并解释一下为什么是这样的结果。