標記化結構初始化語法---結構體成員前加小數點

文章出處:http://blog.csdn.net/adaptiver/article/details/7494081

                    http://blog.csdn.net/windy_net/article/details/6103955

 

對結構體

  1. struct a {  
  2.    
  3. int b;  
  4.    
  5. int c;  
  6.    
  7. }  

有幾種初始化方式:

  1. struct a a1 = {  
  2.   .b = 1,  
  3.   .c = 2  
  4.  };  

或者

  1. struct a a1 = {  
  2.   b:1,  
  3.   c:2  
  4.  }  

或者

  1. struct a a1 = {1, 2};  

內核喜歡用第一種,使用第一種和第二種時,成員初始化順序可變

 

標記化結構初始化語法

在Linux2.6內核中對結構體的定義形式發生了變化,不再支持原來的定義形式。

  1. 1  static struct tty_operations uart_ops =  
  2. 2  {  
  3. 3   .open  = uart_open,//串口打開  
  4. 4   .close  = uart_close,//串口關閉  
  5. 5   .write  = uart_write,//串口發送  
  6. 6   .put_char = uart_put_char,//...  
  7. 7   .flush_chars = uart_flush_chars,  
  8. 8   .write_room = uart_write_room,  
  9. 9   .chars_in_buffer= uart_chars_in_buffer,  
  10. 10  .flush_buffer = uart_flush_buffer,  
  11. 11  .ioctl  = uart_ioctl,  
  12. 12  .throttle = uart_throttle,  
  13. 13  .unthrottle = uart_unthrottle,  
  14. 14  .send_xchar = uart_send_xchar,  
  15. 15  .set_termios = uart_set_termios,  
  16. 16  .stop  = uart_stop,  
  17. 17  .start  = uart_start,  
  18. 18  .hangup  = uart_hangup,  
  19. 19  .break_ctl = uart_break_ctl,  
  20. 20  .wait_until_sent= uart_wait_until_sent,  
  21. 21 #ifdef CONFIG_PROC_FS  
  22. 22  .read_proc = uart_read_proc, //proc入口讀函數  
  23. 23 #endif  
  24. 24  .tiocmget = uart_tiocmget,  
  25. 25  .tiocmset = uart_tiocmset,  
  26. 26 };  


這個聲明採用了標記化結構初始化語法這種寫法是值得采用的,因爲它使驅動程序在結構的定義發生變化時更具有可移植性,並且使代碼更加緊湊且易讀。標記化的初始化方法允許對結構成員進行重新排列。在某些場合下,將頻繁被訪問的成員放在相同的硬件緩存行上,將大大提高性能

---LLD3

 

標記化結構初始化語法是ISO C99的用法

C Primer Plus第五版相關章節:

已知一個結構,定義如下:

  1. struct book  
  2. {  
  3.     char title[MAXTITL];  
  4.     char author[MAXAUTL];  
  5.     float value;  
  6. };  

C99支持結構的指定初始化項目,其語法與數組的指定初始化項目近似。只是,結構的指定初始化項目使用點運算符和成員名(而不是方括號和索引值)來標識具體的元素。例如,只初始化book結構的成員vlaue,可以這樣做:

  1. struct book surprise = { .value = 10.99 };  

可以按照任意的順序使用指定初始化項目:

  1. struct book gift = {  
  2.    
  3.     .value = 25.99,   
  4.     .author = "James Broadfool",   
  5.     .title = "Rue for the Toad"  
  6. };  

正像數組一樣,跟在一個指定初始化項目之後的常規初始化項目爲跟在指定成員後的成員提供了初始值。另外,對特定成員的最後一次賦值是它實際獲得的值。例如,考慮如下聲明:

  1. struct book gift = {  
  2.    
  3.         .value = 18.90,   
  4.         .author = "Philionna pestle",   
  5.         0.25  
  6.     };  


這將把值0.25賦給成員vlaue,因爲它在結構聲明中緊跟在author成員之後。新的值0.25代替了早先的賦值18.90。

有關designated initializer的進一步信息可以參考C99標準的6.7.8節Initialization。

 

代碼舉例:

  1. #include <stdio.h>   
  2. #include <stdlib.h>   
  3. struct operators   
  4. {   
  5.      void (*read1)(char *);   
  6.      void (*read2)(char *);   
  7.      void (*read3)(char *);   
  8.      int n;   
  9. };   
  10.    
  11. void read1(char *data)   
  12. {   
  13.      printf("read1: %s/n",data);   
  14. }   
  15. void read2(char *data)   
  16. {   
  17.      printf("read2: %s/n",data);   
  18. }   
  19. void read3(char *data)   
  20. {   
  21.      printf("read3: %s/n",data);   
  22. }   
  23.    
  24. int main()   
  25. {    //傳統的初始化方法   
  26.      //struct operators my_op = {read1, read2, read3, 100};    //所謂的標記化結構初始化語法   
  27.      struct operators my_op = {.read2 = read2,   
  28.                               .read1 = read1,   
  29.                               .read3 = read3,   
  30.                               .n = 100};   
  31.      my_op.read1("wangyang");   
  32.      my_op.read2("wangyang");   
  33.      my_op.read3("wangyang");   
  34.      return 0;   
  35. }  


重點就在於main()函數中對my_op結構體的初始化語句,使用點加變量名進行初始化。用過Python的人會馬上感覺這與關鍵字傳參是多麼的相似。

那它好處在哪裏呢?

我想好處有三。

首先,標記傳參不用理會參數傳遞的順序,正如我上面的例子裏表示的那樣,我是先初始化了read2,然後再初始化了read1,程序員不用記憶參數的順序;

再者,我們可以選擇性傳參,在傳統C語言順序傳參中,如果你只想對第三個變量進行初始化,那麼你不得不給第一個,第二個參數進行初始化,而有時候一個變量並沒有和合適的默認值,而使用標記初始化法,你可以相當自由地對你有把握的參數進行初始化;

還有,擴展性更好,如果你要在該結構體中增加一個字段,傳統方式下,爲了考慮代碼修改量,你最好將新添加的字段放在這個結構體的最後面,否則你將要面對大量而且無趣的修改,你可能覺得放在哪裏沒什麼關係,但是我們都習慣了,姓名下面是性別,性別下面是年齡,接着是興趣愛好,最後是事蹟描述,如果年齡放在了最後面,難道不彆扭麼?

上面的例程爲什麼在VC++6.0中編譯不同通過呢???

在bluedrum的空間中有篇名爲《C版本差異 --- 結構處理差別》的第3點中講到:

在標準C中(C89),結構標準初始化是用{}來初始化,在C99的版本,採用了可讀性更強的標記化初始化,這在Linux內核和驅動中很爲常見。

其中VC++6.0只支持C89初始化,GCC支持自己標記化或自己擴展初始化。

  1. struct name_str{  
  2.  int data;  
  3.  char name[120];  
  4.  int num;  
  5. };  
  6. /* 標記式初始化,注意順序不同,並可缺省 */  
  7. struct name_str str ={  
  8.   .num = 100;  
  9.   .name = "hxy";  
  10.     
  11. };  
  12.   
  13. /* C89 初始化 */  
  14. struct name_str str2 =  
  15. {  
  16.  100,"Andrew Huang",-2  
  17. };  
  18.   
  19. /* gcc 擴展初始化 */  
  20. struct name_str str3 =  
  21. {  
  22.   name:"bluedrum";  
  23.   data:-1  
  24. }  
  25. }  

個人想編譯以上代碼,想下個C99編譯器,在百度搜索 C99編譯器,解決時間:2009-07-10 21:54回答是

“VC++ 2005 支持的是C89  而不是C99  這點可以在一次對VS2005的負責人的採訪中看出來,他解釋了爲什麼VS2005支持C89 而不支持C99  目前完全支持C99標準的編譯器還不存在 支持部分C99標準的編譯器也不多 做的最好的是GCC”


特定的初始化

標準C89需要初始化語句的元素以固定的順序出現,和被初始化的數組或結構體中的元素順序一樣。

在ISO C99中,你可以按任何順序給出這些元素,指明它們對應的數組的下表或結構體的成員名,並且GNU C也把這作爲C89模式下的一個擴展。這個擴展沒有在GNU C++中實現。

爲了指定一個數組下標,在元素值的前面寫上"[index] = "。比如:

  1. int a[6] = { [4] = 29, [2] = 15 };  

相當於:

  1. int a[6] = { 0, 0, 15, 0, 29, 0 };  

下標值必須是常量表達式,即使被初始化的數組是自動的。

一個可替代的語法是在元素前面寫上".[index]",沒有"=",但從GCC 2.5開始就不再被使用,但GCC仍然接受。爲了把一系列的元素初始化化爲相同的值,寫爲"[first ... ... last] = value"。這是一個GNU擴展。比如:

[html] view plaincopy
  1. int widths[] = { [0 ... 9] = 1, [10 ... 99] = 2, [100] = 3 };  
如果其中的值有副作用,這個副作用將只發生一次,而不是範圍內的每次初始化一次。

注意:數組的長度是指定的最大值加一。

在結構體的初始化語法中,在元素值的前面用".fieldname = "指定要初始化的成員名。例如,給定下面的結構體:

  1. struct point { int x, y; };  
和下面的初始化:

  1. struct point p = { .y = yvalue, .x = xvalue };  
等價於:

  1. struct point p = { xvalue, yvalue };  
另一有相同含義的語法是“.fieldname:”,不過從GCC 2.5開始廢除了,就像這裏所示:

  1. struct point p = { y: yvalue, x: xvalue };  

"[index]"或".fieldname"就是指示符。在初始化共同體時,你也可以使用一個指示符(或不再使用的冒號語法),來指定共同體的哪個元素應該使用。比如:

  1. union foo { int i; double d; };  
  2. union foo f = { .d = 4 };  
將會使用第二個元素把4轉換成一個double類型來在共同體存放。相反,把4轉換成union foo類型將會把它作爲整數i存入共同體,既然它是一個整數。(參考5.24節共同體類型轉換)

你可以把這種命名元素的技術和連續元素的普通C初始化結合起來。每個沒有指示符的初始化元素應用於數組或結構體中的下一個連續的元素。比如:

  1. int a[6] = { [1] = v1, v2, [4] = v4 };  

等價於

  1. int a[6] = { 0, v1, v2, 0, v4, 0 };  
當下標是字符或者屬於enum類型時,標識數組初始化語句的元素特別有用。例如:

  1. int whitespace[256]  
  2.             = { [' '] = 1, ['\t'] = 1, ['\h'] = 1,  
  3.                 ['\f'] = 1, ['\n'] = 1, ['\r'] = 1 };  
你也可以在"="前面協商一系列的".fieldname"和"[index]"指示符來指定一個要初始化的嵌套的子對象;這個列表是相對於和最近的花括號對一致的子對象。比如,用上面的struct point聲明:

  1. struct point ptarray[10] = { [2].y = yv2, [2].x = xv2, [0].x = xv0 };  
如同一個成員被初始化多次,它將從最後一次初始化中取值。如果任何這樣的覆蓋初始化有副作用,副作用的發生與否是非指定的。目前,GCC會捨棄它們併產生一個警告。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章