3. 数据类型标志

在上一节中,我们通过一个复数存储表示抽象层把complex_struct结构体的存储格式和上层的复数运算函数隔开,complex_struct结构体既可以采用直角座标也可以采用极座标存储。但有时候需要同时支持两种存储格式,比如先前已经采集了一些数据存在计算机中,有些数据是以极座标存储的,有些数据是以直角座标存储的,如果要把这些数据都存到complex_struct结构体中怎么办?一种办法是规定complex_struct结构体采用直角座标格式,直角座标的数据可以直接存入complex_struct结构体,而极座标的数据先转成直角座标再存,但由于浮点数的精度有限,转换总是会损失精度的。这里介绍另一种办法,complex_struct结构体由一个数据类型标志和两个浮点数组成,如果数据类型标志为0,那么两个浮点数就表示直角座标,如果数据类型标志为1,那么两个浮点数就表示极座标。这样,直角座标和极座标的数据都可以适配(Adapt)到complex_struct结构体中,无需转换和损失精度:

  1. enum coordinate_type { RECTANGULAR, POLAR };
  2. struct complex_struct {
  3. enum coordinate_type t;
  4. double a, b;
  5. };

enum关键字的作用和struct关键字类似,把coordinate_type这个标识符定义为一个Tag,struct complex_struct表示一个结构体类型,而enum coordinate_type表示一个枚举(Enumeration)类型。枚举类型的成员是常量,它们的值由编译器自动分配,例如定义了上面的枚举类型之后,RECTANGULAR就表示常量0,POLAR表示常量1。如果不希望从0开始分配,可以这样定义:

  1. enum coordinate_type { RECTANGULAR = 1, POLAR };

这样,RECTANGULAR就表示常量1,而POLAR表示常量2。枚举常量也是一种整型,其值在编译时确定,因此也可以出现在常量表达式中,可以用于初始化全局变量或者作为case分支的判断条件。

有一点需要注意,虽然结构体的成员名和变量名不在同一命名空间中,但枚举的成员名却和变量名在同一命名空间中,所以会出现命名冲突。例如这样是不合法的:

  1. int main(void)
  2. {
  3. enum coordinate_type { RECTANGULAR = 1, POLAR };
  4. int RECTANGULAR;
  5. printf("%d %d\n", RECTANGULAR, POLAR);
  6. return 0;
  7. }

complex_struct结构体的格式变了,就需要修改复数存储表示层的函数,但只要保持函数接口不变就不会影响到上层函数。例如:

  1. struct complex_struct make_from_real_img(double x, double y)
  2. {
  3. struct complex_struct z;
  4. z.t = RECTANGULAR;
  5. z.a = x;
  6. z.b = y;
  7. return z;
  8. }
  9.  
  10. struct complex_struct make_from_mag_ang(double r, double A)
  11. {
  12. struct complex_struct z;
  13. z.t = POLAR;
  14. z.a = r;
  15. z.b = A;
  16. return z;
  17. }

习题

1、本节只给出了make_from_real_imgmake_from_mag_ang函数的实现,请读者自己实现real_partimg_partmagnitudeangle这些函数。

2、编译运行下面这段程序:

  1. #include <stdio.h>
  2.  
  3. enum coordinate_type { RECTANGULAR = 1, POLAR };
  4.  
  5. int main(void)
  6. {
  7. int RECTANGULAR;
  8. printf("%d %d\n", RECTANGULAR, POLAR);
  9. return 0;
  10. }

结果是什么?并解释一下为什么是这样的结果。