在閱讀GNU/Linux核心代碼時,我們會遇到一種特殊的結構初始化方式。該方式是某些C教材(如譚二版、K&R二版)中沒有介紹過的。這種方式稱為指定初始化(designated initializer)。下面我們看一個例子,Linux-2.6.x/drivers/usb/storage/usb.c中有這樣一個結構體初始化項目:
static struct usb_driver
usb_storage_driver = {
.owner = THIS_MODULE,
.name = "usb-storage",
.probe = storage_probe,
.disconnect =
storage_disconnect,
.id_table =
storage_usb_ids,
};
乍一看,這與我們之前學過的結構體初始化差距甚遠。其實這就是前面所說的指定初始化在Linux裝置驅動程式中的一個應用,它源自ISO
C99标準。以下我摘錄了C Primer
Plus第五版中相關章節的内容,進而就可以很好的了解2.6版核心采用這種方式的優勢就在于由此初始化不必嚴格按照定義時的順序。這帶來了極大的靈活性,其更大的益處還有待大家在開發中結合自身的應用慢慢體會。
已知一個結構,定義如下
struct book {
char title[MAXTITL];
char author[MAXAUTL];
float value;
C99支援結構的指定初始化項目,其文法與數組的指定初始化項目近似。隻是,結構的指定初始化項目使用點運算符和成員名(而不是方括号和索引值)來辨別具體的元素。例如,隻初始化book結構的成員value,可以這樣做:
struct book surprise = { .value
= 10.99 };
可以按照任意的順序使用指定初始化項目:
struct book gift = {
.value = 25.99,
.author = "James
Broadfool",
.title = "Rue for the
Toad"};
正像數組一樣,跟在一個指定初始化項目之後的正常初始化項目為跟在指定成員後的成員提供了初始值。另外,對特定成員的最後一次指派是它實際獲得的值。例如,考慮下列聲明:
struct book gift = { .value =
18.90,
.author = "Philionna
pestle",
0.25};
這将把值0.25賦給成員value,因為它在結構聲明中緊跟在author成員之後。新的值0.25代替了早先的指派18.90。
有關designated initializer的進一步資訊可以參考c99标準的6.7.8節Ininialization。
這篇轉載大緻解決了我們疑惑的問題。其實很簡單,繞來繞去的,不就是初始化嗎!
但是随後又在c語言擴充中找到了如下的起碼是我不知道的事情。
标準C89需要初始化語句的元素以固定的順序出現,和被初始化的數組或結構體中的元素順序一樣。在ISO
C99中,你可以按任何順序給出這些元素,指明它們對應的數組的下标或結構體的成員名,并且GNU
C也把這作為C89模式下的一個擴充。這個擴充沒有在GNU C++中實作。
為了指定一個數組下标,在元素值的前面寫上“[index]
=”。比如:
int a[6] = { [4] = 29, [2] = 15 };
相當于:
int a[6] = { 0, 0, 15, 0, 29, 0 };
下标值必須是常量表達式,即使被初始化的數組是自動的。
一個可替代這的文法是在元素值前面寫上“.[index]”,沒有“=”,但從GCC
2.5開始就不再被使用,但GCC仍然接受。為了把一系列的元素初始化為相同的值,寫為“[first ... last] =
value”。這是一個GNU擴充。比如:
int widths[] = { [0 ... 9] = 1, [10 ... 99] = 2, [100] = 3 };
如果其中的值有副作用,這個副作用将隻發生一次,而不是範圍内的每次初始化一次。
注意,數組的長度是指定的最大值加一。
在結構體的初始化語句中,在元素值的前面用“.fieldname = ”指定要初始化的成員名。例如,給定下面的結構體,
struct point { int x, y; };
和下面的初始化,
struct point p = { .y = yvalue, .x = xvalue };
等價于:
struct point p = { xvalue, yvalue };
另一有相同含義的文法是“.fieldname:”,不過從GCC 2.5開始廢除了,就像這裡所示:
struct point p = { y: yvalue, x: xvalue };
“[index]”或“.fieldname”就是訓示符。在初始化共同體時,你也可以使用一個訓示符(或不再使用的冒号文法),來指定共同體的哪個元素應該使用。比如:
union foo { int i; double d; };
union
foo f = { .d = 4 };
将會使用第二個元素把4轉換成一個double類型來在共同體存放。相反,把4轉換成union
foo類型将會把它作為整數i存入共同體,既然它是一個整數。(參考5.24節向共同體類型轉換。)
你可以把這種命名元素的技術和連續元素的普通C初始化結合起來。每個沒有訓示符的初始化元素應用于數組或結構體中的下一個連續的元素。比如,
int a[6] = { [1] = v1, v2, [4] = v4 };
等價于
int a[6] = { 0, v1, v2, 0, v4, 0 };
當下标是字元或者屬于enum類型時,辨別數組初始化語句的元素特别有用。例如:
int whitespace[256]
= { [' '] = 1, ['\t'] = 1, ['\h'] = 1,
['\f'] = 1, ['\n'] = 1, ['\r'] = 1 };
你也可以在“=”前面寫上一系列的“.fieldname”和“[index]”訓示符來指定一個要初始化的嵌套的子對象;這個清單是相對于和最近的花括号對一緻的子對象。比如,用上面的struct
point聲明:
struct point ptarray[10] = {
[2].y = yv2, [2].x = xv2, [0].x = xv0 };
如果同一個成員被初始化多次,它将從最後一次初始化中取值。如果任何這樣的覆寫初始化有副作用,副作用發生與否是非指定的。目前,gcc會舍棄它們并産生一個警告。

如果這篇文章幫助到了你,你可以請作者喝一杯咖啡