C語言常常讓人覺得它所能表達的東西非常有限。它不具有類似第一級函數和模式匹配這樣的高級功能。但是C非常簡單,並且仍然有一些非常有用的語法技巧和功能,只是沒有多少人知道罷了。
指定的初始化
很多人都知道像這樣來靜態地初始化數組:
int fibs[] = {1, 1, 2, 3, 5};
C99標准實際上支持一種更為直觀簡單的方式來初始化各種不同的集合類數據(如:結構體,聯合體和數組)。
數組
我們可以指定數組的元素來進行初始化。這非常有用,特別是當我們需要根據一組#define來保持某種映射關系的同步更新時。來看看一組錯誤碼的定義,如:
/* Entries may not correspond to actual numbers. Some entries omitted. */ #define EINVAL 1 #define ENOMEM 2 #define EFAULT 3 /* ... */ #define E2BIG 7 #define EBUSY 8 /* ... */ #define ECHILD 12 /* ... */
現在,假設我們想為每個錯誤碼提供一個錯誤描述的字符串。為了確保數組保持了最新的定義,無論頭文件做了任何修改或增補,我們都可以用這個數組指定的語法。
char *err_strings[] = { [0] = "Success", [EINVAL] = "Invalid argument", [ENOMEM] = "Not enough memory", [EFAULT] = "Bad address", /* ... */ [E2BIG ] = "Argument list too long", [EBUSY ] = "Device or resource busy", /* ... */ [ECHILD] = "No child processes" /* ... */ };
這樣就可以靜態分配足夠的空間,且保證最大的索引是合法的,同時將特殊的索引初始化為指定的值,並將剩下的索引初始化為0。
宏列表
C中的一個慣用方法,是說有一個已命名的實體列表,需要為它們中的每一個建立函數,將它們中的每一個初始化,並在不同的代碼模塊中擴展它們的名字。這在 Mozilla的源碼中經常用到,我就是在那時學到這個技巧的。例如,在我去年夏天工作的那個項目中,我們有一個針對每個命令進行標記的宏列表。其工作方 式如下:
#define FLAG_LIST(_) \ _(InWorklist) \ _(EmittedAtUses) \ _(LoopInvariant) \ _(Commutative) \ _(Movable) \ _(Lowered) \ _(Guard)
它定義了一個FLAG_LIST宏,這個宏有一個參數稱之為 _ ,這個參數本身是一個宏,它能夠調用列表中的每個參數。舉一個實際使用的例子可能更能直觀地說明問題。假設我們定義了一個宏DEFINE_FLAG,如:
#define DEFINE_FLAG(flag) flag, enum Flag { None = 0, FLAG_LIST(DEFINE_FLAG) Total }; #undef DEFINE_FLAG
對FLAG_LIST(DEFINE_FLAG)做擴展能夠得到如下代碼:
enum Flag { None = 0, DEFINE_FLAG(InWorklist) DEFINE_FLAG(EmittedAtUses) DEFINE_FLAG(LoopInvariant) DEFINE_FLAG(Commutative) DEFINE_FLAG(Movable) DEFINE_FLAG(Lowered) DEFINE_FLAG(Guard) Total };
接着,對每個參數都擴展DEFINE_FLAG宏,這樣我們就得到了enum如下:
enum Flag { None = 0, InWorklist, EmittedAtUses, LoopInvariant, Commutative, Movable, Lowered, Guard, Total };
接着,我們可能要定義一些訪問函數,這樣才能更好的使用flag列表:
#define FLAG_ACCESSOR(flag) \ bool is##flag() const {\ return hasFlags(1 << flag);\ }\ void set##flag() {\ JS_ASSERT(!hasFlags(1 << flag));\ setFlags(1 << flag);\ }\ void setNot##flag() {\ JS_ASSERT(hasFlags(1 << flag));\ removeFlags(1 << flag);\ } FLAG_LIST(FLAG_ACCESSOR) #undef FLAG_ACCESSOR
一步步的展示其過程是非常有啟發性的,如果對它的使用還有不解,可以花一些時間在gcc –E上。