2012-07-19 634 views
5

我剛在C++中遇到了一個令人討厭的bug。所以我有寄存器和值的列表,它們被包裝在一個結構中,然後這些結構被初始化爲一個數組。但後來我意外地輸入了()而不是{}。下面是一些測試代碼:C++和C中括號的含義

#include <stdio.h> 

struct reg_val { 
     unsigned reg; 
     unsigned val; 
}; 

struct reg_val faulty_array[] = { 
     {0x5001, 0xff}, 
     {0x5580, 0x01}, 
     (0x5580, 0x02), //<- THIS LINE IS THE PROBLEM 
     (0x5589, 0x00), //<- AND THIS LINE 
}; 

struct reg_val good_array[] = { 
     {0x5001, 0xff}, 
     {0x5580, 0x01}, 
     {0x5580, 0x02}, 
     {0x5589, 0x00}, 
}; 

int main() 
{ 
     unsigned i; 
     unsigned faulty_size = sizeof(faulty_array)/sizeof(struct reg_val); 
     printf("Size of faulty array: %d\n", faulty_size); 

     for (i = 0; i < faulty_size; ++i) { 
       printf("faulty reg: %x val: %x\n", faulty_array[i].reg, 
         faulty_array[i].val); 
     } 

     unsigned good_size = sizeof(good_array)/sizeof(struct reg_val); 
     printf("\nSize of good array: %d\n", good_size); 
     for (i = 0; i < good_size; ++i) { 
       printf("good reg: %x val: %x\n", good_array[i].reg, 
         good_array[i].val); 
     } 
     return 0; 
} 

我比較熟悉C讓我吃驚,這仍然與G ++編譯:

$ g++ -Wall array.cc 
array.cc:11: warning: left-hand operand of comma has no effect 
array.cc:12: warning: left-hand operand of comma has no effect 
array.cc:13: warning: missing braces around initializer for ‘reg_val’ 
$ ./a.out 
Size of faulty array: 3 
faulty reg: 5001 val: ff 
faulty reg: 5580 val: 1 
faulty reg: 2 val: 0  <-- the first value gets discarded as mentioned in the compiler warning 

Size of good array: 4 
good reg: 5001 val: ff 
good reg: 5580 val: 1 
good reg: 5580 val: 2 
good reg: 5589 val: 0 

此代碼顯然會失敗,C編譯器來編譯,有什麼使C++編譯器(儘管勉強接受)接受此代碼的C++中的差異?

回答

4

要回答你的問題,我先回答:爲什麼這個編譯失敗在C?那麼,它無法編譯由於:

initializer element is not constant 

良好的措施,讓我們在C刪除{} S:

struct reg_val faulty_array[] = { 
     {0x5001, 0xff}, 
     {0x5580, 0x01}, 
     0x5580, 0x02, //<- THIS LINE IS THE PROBLEM 
     0x5589, 0x00, //<- AND THIS LINE 
}; 

現在程序的輸出:

Size of faulty array: 4 
faulty reg: 5001 val: ff 
faulty reg: 5580 val: 1 
faulty reg: 5580 val: 2 
faulty reg: 5589 val: 0 

這完全是允許使用C標準(和C++)。 C(和C++)將大括號展開以初始化結構元素(這會返回)。您的代碼在C中失敗,因爲具有靜態存儲持續時間的對象必須使用常量表達式或包含常量表達式的聚合初始化程序初始化C不會將(0x5580, 0x02)視爲一個常量表達式。

這(不幸)編譯在C++,因爲C++將兩個恆定表達式之間逗號運算符作爲一個常量表達式,所以代碼更像:

struct reg_val faulty_array[] = { 
     {0x5001, 0xff}, 
     {0x5580, 0x01}, 
     0x02, 
     0x00, 
}; 

...是,當然,允許。

struct reg_val faulty_array[] = { 
     {0x5001, 0xff}, 
     {0x5580, 0x01}, 
     {0x02, 0x00}, 
}; 
+0

啊,謝謝。我懂了。這就說得通了。但海灣合作委員會似乎不是很高興我「扁平括號」。 '$ gcc -Wall array.c array.c:13:warning:初始化程序周圍缺少大括號' – Lucas 2012-07-19 17:24:07

+0

'gcc' with'-Wall'也會警告你'if(c = something())',因爲'建議括號用作真值的周圍分配「。只是因爲ANSI C允許的東西並不意味着它不會產生警告。 – 2012-07-19 17:57:04

+0

你是對的,'gcc -std = c89 -pedantic array.c'編譯沒有警告。 – Lucas 2012-07-19 18:03:59

3

C++有一個逗號運算符,用於評估它的兩個操作數並返回其右操作數的值,忽略它的左邊。 You can see it more clearly here.

與C過,顯然=)(感謝,@BenVoigt)

+2

問題是「C++有什麼區別(與C相比)?「答案不是」逗號運算符「 – 2012-07-19 16:20:31

+0

@BenVoigt:我不知道C,我只是解釋它在C++中編譯的原因,我假設他嘗試在C中編譯它,並且該特定部分失敗。 – Ryan 2012-07-19 16:22:15

+0

我會假設C++試圖在編譯時計算這些表達式,並且C沒有,這就解釋了錯誤與C和C++之間的警告 – antlersoft 2012-07-19 16:24:20

4

什麼使你認爲它會失敗在C編譯?

C++:http://ideone.com/KLPh4 C:http://ideone.com/VYUbL

注意你的警告。我無法強調這一點。警告有助於你發現這樣的錯誤。

那麼,C中的錯誤信息使得這個區別變得非常清楚:C要求初始化器是常量,而不是任意的表達式。這是沒有意義的,我爲什麼那些不被認爲是常數,因爲這在C編譯罰款:

+4

它在C-land中編譯失敗,因爲「 「換句話說:'(expr,expr)'不是一個常量表達式或一個聚集初始化。 – 2012-07-19 16:21:04

+0

Ideone在C示例中失敗,出現」編譯錯誤「 – pmr 2012-07-19 16:23:34

+0

@Travis:謝謝,更新了答案。 – 2012-07-19 16:24:05