Skip to content

3. 数据类型标志

在上一节中,我们通过一个复数存储表示抽象层把complex_struct结构体的存储格式和上层的复数运算函数隔开,complex_struct结构体既可以采用直角座标也可以采用极座标存储。但有时候需要同时支持两种存储格式,比如先前已经采集了一些数据存在计算机中,有些数据是以极座标存储的,有些数据是以直角座标存储的,如果要把这些数据都存到complex_struct结构体中怎么办?

一种办法是规定complex_struct结构体采用直角座标格式,直角座标的数据可以直接存入complex_struct结构体,而极座标的数据先转成直角座标再存,但由于浮点数的精度有限,转换总是会损失精度的。这里介绍另一种办法,complex_struct结构体由一个数据类型标志和两个浮点数组成,如果数据类型标志为0,那么两个浮点数就表示直角座标,如果数据类型标志为1,那么两个浮点数就表示极座标。这样,直角座标和极座标的数据都可以适配(Adapt)到complex_struct结构体中,无需转换和损失精度:

c
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开始分配,可以这样定义:

c
enum coordinate_type { RECTANGULAR = 1, POLAR };

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

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

c
int main(void)
{
    enum coordinate_type { RECTANGULAR = 1, POLAR };
    int RECTANGULAR;
    printf("%d %d\n", RECTANGULAR, POLAR);
    return 0;
}

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

c
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_imgmake_from_mag_ang函数的实现,请读者自己实现real_partimg_partmagnitudeangle这些函数。

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

c
#include <stdio.h>

enum coordinate_type { RECTANGULAR = 1, POLAR };

int main(void)
{
    int RECTANGULAR;
    printf("%d %d\n", RECTANGULAR, POLAR);
    return 0;
}

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

上一节 | 目录 | 下一节