3. 数据类型标志
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; }
习题
1、本节只给出了make_from_real_img
和make_from_mag_ang
函数的实现,请读者自己实现real_part
、img_part
、magnitude
、angle
这些函数。
2、编译运行下面这段程序:
#include <stdio.h> enum coordinate_type { RECTANGULAR = 1, POLAR }; int main(void) { int RECTANGULAR; printf("%d %d\n", RECTANGULAR, POLAR); return 0; }
结果是什么?并解释一下为什么是这样的结果。