2014-09-29 120 views
3

這是我的代碼:編譯C結構

#include <stdio.h> 
typedef struct { 
    const char *description; 
    float value; 
    int age; 
} swag; 

typedef struct { 
    swag *swag; 
    const char *sequence; 
} combination; 

typedef struct { 
    combination numbers; 
    const char *make; 
} safe; 

int main(void) 
{ 
    swag gold = { "GOLD!", 100000.0 }; 
    combination numbers = { &gold, "6503" }; 
    safe s = { numbers, "RAMCON" }; 

    printf("Contents = %s\n", s.numbers.swag->description); 

    getchar(); 

    return 0; 
} 

每當我用VS開發者控制檯編譯它,我得到這個錯誤:錯誤C2440:「初始化」:無法從「組合」轉換爲「贓物* 」。 但是,如果我使用gcc控制檯只是打印:「黃金!」。不明白這裏發生了什麼。

+0

@remyabel我正在使用Visual Studio 13開發人員命令提示符。 – 2014-09-29 06:53:48

+0

對不起,我發佈了錯誤編譯器的鏈接。 – 2014-09-29 06:54:38

+0

@remyabel任何線索爲什麼我得到這個錯誤? – 2014-09-29 06:56:13

回答

4

你偶然發現的是在各種C89/90編譯器中使用的流行的非標準編譯器擴展的實現特定變體。

經典C89/90的嚴格規則禁止在{}初始值設定項中使用非常量對象。這立即意味着在初始化器中指定{}之間的整個對象是不可能的,因爲這會違反上述要求。根據該規則,您只能使用{}之間的標量常量。

但是,許多C89/90編譯器忽略該標準要求,並允許用戶在爲局部對象編寫{}初始值設定項時指定非常量值。不幸的是,如果用戶指定的複雜struct對象{}初始化內,如本立即創建了一個模棱兩可的

safe s = { numbers, "RAMCON" }; 

的語言標準並沒有允許這樣做,因爲這個原因,目前還不清楚這numbers初始化應適用什麼至。有兩種方法來解釋這一點:

  1. 語言的現有規則說,編譯器必須自動進入struct嵌套的每個級別,並從{}申請順序初始化,以這種方式找到的所有連續的標量場(實際上,這有點複雜,但這是一般的想法)。

    這正是你的編譯器所做的。它花了第一個初始化程序numbers,發現第一個標量字段s.numbers.swag並試圖將前者應用於後者。這預計會產生你觀察到的錯誤。

  2. 其他編譯器對該擴展采取了更詳細的方法。當編譯器看到{}列表中的下一個初始化程序與左側的目標字段的類型相同時,它沒有「打開」目標字段,也沒有進入下一級嵌套,而是使用整個初始化器的值來初始化整個目標字段。

後者的行爲是你在你的例子所期待的(如果我沒有記錯,這是C99所要求的行爲),但你的C89/90的編譯器按照第一種方法表現。

換句話說,當您編寫C89/90代碼時,當您在本地{}初始值設定項中指定非常量對象時,通常可以使用該非標準擴展。但是避免在這樣的初始值設定項中使用struct對象並且僅使用標量初始值設定項是個好主意。

0

看起來像初始值設定項的問題。如果你使用正確的選項用gcc,它會告訴你:

$ gcc -Wall -ansi -pedantic x.c 
x.c: In function ‘main’: 
x.c:21: warning: initializer element is not computable at load time 
x.c:22: warning: initializer element is not computable at load time 

這是propably了同樣的問題VS是想告訴你。如果聲明goldnumbers爲靜態,則可以使這些消失。

+0

我爲什麼要將它們初始化爲靜態? – 2014-09-29 07:07:03

+0

@AnasAyubi使元素在加載時可計算。 – Jens 2014-09-29 08:55:13