2011-07-07 54 views
9

我在一個項目中發現了一段奇怪的代碼,我必須維護它。有一個類的空數組成員,不會導致編譯器錯誤。我測試過的這樣的一些代碼的變化與MSVC 10.0:空數組聲明 - 奇怪的編譯器行爲

template<class T> struct A { 
    int i[]; 
}; // warning C4200: nonstandard extension used : zero-sized array in struct/union 

template<class T> struct B { static int i[]; }; 
template<class T> int B<T>::i[]; 

struct C { 
    int i[]; 
}; //warning C4200: nonstandard extension used : zero-sized array in struct/union 

template<class T> struct D { static int i[]; }; 
template<class T> int D<T>::i[4]; 
template<>  int D<int>::i[] = { 1 }; 


int main() 
{ 
    A<void> a; 
    B<void> b; 
    C c; 
    D<void> d0; 
    D<int> d1; 

    a.i[0] = 0;  // warning C4739: reference to variable 'a' exceeds its storage space 

    b.i[0] = 0;  // warning C4789: destination of memory copy is too small 

    c.i[0] = 0;  // warning C4739: reference to variable 'c' exceeds its storage space 

    int i[];  // error C2133: 'i' : unknown size 

    d0.i[0] = 0; // ok 
    d0.i[1] = 0; // ok 

    return 0; 
} 

錯誤消息在int i[]絕對是明智的我。與類D一起顯示的代碼是格式良好的標準C++。但是關於類ABC?這個類中的成員變量int i[]是什麼類型的?

回答

6

編輯:

您的懷疑是由definition of the extension to the language,這允許零大小的數組在結構/聯合的端說明。我沒有嘗試過,但是如果你在零大小的數組之後聲明另一個成員,它應該失敗。

所以,如果你在棧上分配一個變量,你必須知道它的大小;規則的例外情況是在struct/union的末尾分配一個數組,這時可能會出現一些C典型的詭計。

在C++中,這引發了一個警告,因爲默認的拷貝構造函數和賦值操作符可能不起作用。

以前的答案:

編譯器警告你這個事實,你正在試圖定義零大小的數組。這在標準的C/C++中是不允許的。

讓我們看看班級的差異。

在d類:

template<class T> struct D { static int i[]; };

它的作品,因爲你只是聲明一個靜態成員變量的類型。對於這個鏈接,你也需要定義實際的陣列,在定義語句像你這樣:

template<>  int D<int>::i[] = { 1 }; 

在這裏你還可以通過初始化指定數組的大小。

B級,你在做類似的事情,但定義是:

template<class T> int B<T>::i[]; 

即不指定大小,並得到了警告。

對於類A,更多相同,您正在定義不帶大小的數組類型的成員變量。

+0

問題是:爲什麼警告(與類'A','B'和'C'有關)警告而不是錯誤?在我看來,這與我在局部變量聲明中得到的錯誤相比是不對稱的。 – 0xbadf00d

+0

請參閱我的編輯。 – sergio

+0

謝謝,另一個「漂亮」的微軟擴展到C++標準...... – 0xbadf00d

0

好的。只是爲了確定,你想知道爲什麼編譯器不會將它標記爲錯誤嗎?在這種情況下,我認爲這個問題在編譯器中是不可預測的,但我一直都知道MSVC會發生這種情況。

http://support.microsoft.com/kb/98409

讓我看看我能解釋它像他們做到了。如果我聲明一個結構與這樣的一個空數組,

struct a 
{ 
    int x; 
    char empty[]; 
}; 

編譯器可能分配4個字節爲x和可能另外4個字節用於字符指針。空將包含結構a開始之後的4個字節的地址。

由於它是一個沒有長度的字符數組,試圖訪問它將是一個錯誤,因爲沒有尾隨0來表示字符串的結尾。

我可以選擇稍後初始化結構以指向實際字符串的開始以克服此錯誤。

struct a myStruct = { 1, "hello world"}; // empty now points to the start of "hello world" 

由於結構基本上是一類,原來你可以做同樣的事情用一個類,如果你要確保它的聚集,而不是一個完整的類。

所以,你去了。當在struct/class中聲明時,MSVC編譯器將沒有固定大小的數組視爲指針。請記住,類定義僅僅是聲明。編譯器不會爲它們分配空間,除非您爲其創建實例。當你開始思考它時,它就是從此開始的。編譯器如何知道您是否打算稍後爲其分配存儲空間。它變成了運行時工件,但編譯器仍然足夠聰明,可以警告你這個問題。