C89,C99: C數組&結構體&聯合體快速初始化


1. 背景

     C89標准規定初始化語句的元素以固定順序出現,該順序即待初始化數組或結構體元素的定義順序。

     C99標准新增指定初始化(Designated Initializer),即可按照任意順序對數組某些元素或結構體某些成員進行選擇性初始化,只需指明它們所對應的數組下標或結構體成員名。GNU C將其作為C89模式的擴展。

     借助指定初始化特性,可實現數組或結構體元素的快速初始化。

2. 數組初始化

   在數組初始化列表中使用“[index常量表達式]=value”形式可對index所指定的某個元素進行初始化。如:

int arr[6] = { [0]=5, [1]=6, [3] =10, [4]=11 }; 或

int arr[6] = { [0]=5, 6, [3] =10, 11 }; 或

int arr[6] = { [3] =10, 11, [0]=5, 6 }; (指定順序可變)

均等效於:int arr[6] = {5, 6, 0, 10, 11, 0};

說明:

     1) 若在某個指定初始化項目后跟有不至一個值,如[3]=10,11。則多出的數值用於對后續的數組元素進行初始化,即數值11用來初始化arr[4]。

     2) C數組初始化一個或多個元素后,未初始化的元素將被自動地初始化為0或NULL(針對指針變量)。未經過任何初始化的數組,所有元素的值都是不確定的。

     當下標是字符或屬於枚舉類型時,標識數組初始化語句的元素特別有用。如:

int whitespace[256] = {

    [' '] = 1, ['\t'] = 1, ['\h'] = 1,

    ['\f'] = 1, ['\n'] = 1, ['\r'] = 1 };

static const char* gMsgName[] = {

    [MSG_CREATE] = "Create",

    [MSG_DELETE] = "Delete",

    [MSG_SET]    = "Set",

    [MSG_GET]    = "Get",

    [MSG_GET_ALARMS] = "GetAlarms",

    [MSG_SET_TABLE]  = "SetTable"}; //枚舉值變化時,數組自動同步更新

這種初始化方式可實現簡化的映射表,不過在下標指示符跨度較大時稍顯浪費內存。

 

GNU C還支持”[first … last]=value”(…兩側有空格)的形式,將該范圍內的若干元素初始化為相同值。如:

int arr[]={ [0 ... 3]=1, [4 ... 5]=2, [6 ... 9] =3}; 或

int arr[]={ [0 ... 3]=1, [4 ... 5]=2, [6 ... 8] =3, [9] =3};

均等效於:int arr[10] = {1, 1, 1, 1, 2, 2, 3, 3, 3, 3};

注意,數組長度為指定的最大下標值加1。

 

這種初始化方式比memset高效且用途更廣,如:

int arr[]={ [0 ... 127]=-1 };

等效於:memset(arr, 0xFF, sizeof(arr));

int arr[]={ [0 ... 127]=1 };

不等效於:memset(arr, 1, sizeof(arr));

 

3. 結構體初始化

 結構的指定初始化語法與數組類似,只不過使用點運算符和成員名(而不是方括號和索引值)標識具體的元素。例如,對於結構體

     struct Structure{ int a; int b; };或struct Structure{ int a, b; };

     有以下幾種初始化方式:

struct Structure tStct = {

    .a = 1,

    .b = 2

};

用“.fieldname=”指定待初始化成員名(成員初始化順序可變)
struct Structure tStct = {

    a : 1,

    b : 2

};

用“fieldname:”指定待初始化成員名(成員初始化順序可變)

GCC 2.5已廢除,但仍接受
struct Structure tStct = { 1, 2};

 

內核結構體多采用第一種初始化方式,如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,

};

 

  該方式初始化時不必嚴格按照定義時的順序,靈活性很高。

    【例】定義如下結構體

struct book{

    char title[MAXTITL];

    char author[MAXAUTL];

    float value;

    int mask[128];

};

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

struct book gift = { .value = 25.99,

                           .author = "James Broadfool",

                           .title = "Rue for the Toad",

                           .mask[0 ... 127] = -1};

 也可只初始化結構體成員value:

struct book surprise = { .value = 10.99 };

正如數組一樣,跟在指定初始化項目后且沒有指示符(“[index]”或“fieldname”)的常規初始化項目為跟在指定成員后定義的下個成員提供初始值。此外,若對特定成員初始化多次,則最后一次賦值是它實際獲得的值。

     考慮下列聲明:

struct book gift = { .value = 18.90,

                           .author = "Philionna pestle",

                            0.25};

將把值0.25賦給成員value,因為它在結構體定義中緊跟在author成員之后。新值0.25覆蓋前值18.90。

若覆蓋初始化有副作用(如類型不兼容),則GNU C可能會產生編譯警告。

聯合體初始化

可用“.fieldname” (或已廢棄的“fieldname:”)指示符來指定使用聯合體的哪個元素,如:

union UnionT { int i; double d; };

union UnionT tUnion = { .d = 4 };

使用第二個元素將4轉換為double類型存入聯合體。相反,將4轉換為union UnionT類型則會把它作為整數i存入聯合體。

 

5 結構體數組初始化

     可在“=”前寫上一系列的“[index]”和“.fieldname”指示符來指定待初始化的嵌套子對象,如:

struct Structure ptStct[10] = {

     [2].b = 0x2B, [2].a = 0x2A,

     [0].a = 0x0A };

6 GCC擴展結構體賦值

     對於上文定義的結構體Structure可整體賦值:

struct Structure tStct1, tStct2;

tStct2 = tStct1;

 結構體也可作為函數返回值對另一個結構體賦值:

struct Structure func1();

struct Structure tStct = func1();

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM