以前在看Linux代碼時,就對它的結構體初始化寫法感到奇怪,所有的初始化代碼都寫清了變數名,並且變數名前面還有一個詭異的點。最近學習Linux裝置驅動,又遇到了,就查了一下,發現自己的知識果然紕漏不少,此種初始化寫法並不是什麼特殊的代碼風格,而是所謂的C語言標記化結構初始化文法(designated initializer),而且還是一個ISO標準。
[cpp] view plaincopy
- #include <stdio.h>
- #include <stdlib.h>
- struct operators
- {
- void (*read1)(char *);
- void (*read2)(char *);
- void (*read3)(char *);
- int n;
- };
-
- void read1(char *data)
- {
- printf("read1: %s/n",data);
- }
- void read2(char *data)
- {
- printf("read2: %s/n",data);
- }
- void read3(char *data)
- {
- printf("read3: %s/n",data);
- }
-
- int main()
- { //傳統的初始化方法
- //struct operators my_op = {read1, read2, read3, 100}; //所謂的標記化結構初始化文法
- struct operators my_op = {.read2 = read2,
- .read1 = read1,
- .read3 = read3,
- .n = 100};
- my_op.read1("wangyang");
- my_op.read2("wangyang");
- my_op.read3("wangyang");
- return 0;
- }
重點就在於main()函數中對my_op結構體的初始化語句,使用點加變數名進行初始化。用過python的人會馬上感覺到這與關鍵字傳參是多麼的相似。
那它的好處在哪裡呢?我想好處有三:
首先,標記傳參不用理會參數傳遞的順序,正如我上面的例子表示的那樣,我是先初始化了read2,然後再初始化了read1,程式員不用記憶參數的順序;
其次,我們可以選擇性傳參,在傳統C語言順序傳參中,如果你只想對第三個變數進行初始化,那麼你不得不給第一個, 第二個參數進行初始化,而有時候一個變數並沒有很合適的預設值,而使用標記初始化法,你可以相當自由地對你有把握的參數進行初始化;
第三,擴充性更好,如果你要在該結構體中增加一個欄位,傳統方式下,為了考慮代碼修改量,你最好將新添加的欄位放在這個結構體的最後面,否則你將要面對大量且無趣的修改,你可能覺得放在哪裡沒什麼關係,但是我們都習慣了,姓名下面是性別,性別下面是年齡,接著是興趣愛好,最後是事迹描述,如果年齡放在了最後面,難道不彆扭嗎?!
有人提到,該種文法還有利於提高效能,木有感覺出來,我在這裡就不談這點了。
其實,該種初始化文法並不是什麼新技術,新定義,它就是ISO C99的一個標準用法,也就是說99年就有了,再說Linus也不會去趕什麼時髦的,據說C Primer Plus第五版中提到了這點,不過,我沒有看過該書,遺憾,我是直接投入了物件導向的懷抱。
GCC有擴充標記化結構初始化文法,寫法是下面這樣的:
struct operators my_op = {read2 : read2, read1 : read1, read3 : read3,};
轉自: http://www.hustyx.com/cplusplus/agtodXN0eWl4aWFuZ3IPCxIHQXJjaGl2ZRihigcM