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_imgmake_from_mag_ang函數的實現,請讀者自己實現real_partimg_partmagnitudeangle這些函數。

2、編譯運行下面這段程序:

#include <stdio.h>

enum coordinate_type { RECTANGULAR = 1, POLAR };

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

結果是什麼?並解釋一下為什麼是這樣的結果。