2008-11-17 171 views
48

我正在重構一些舊代碼,並且發現了幾個包含零長度數組的結構(見下文)。警告當然會受到編譯指示的壓制,但是我沒有通過包含這種結構的「新」結構創建(錯誤2233)。數組'byData'用作指針,但爲什麼不使用指針呢?或長度爲1的數組?當然,沒有評論加入讓我享受這個過程...... 任何原因使用這樣的事情?任何重構這些建議?零長度數組

struct someData 
{ 
    int nData; 
    BYTE byData[0]; 
} 

NB這是C++,Windows XP中,VS 2003

+3

這是[comp.lang.c FAQ](http://www.c-faq.com/)問題2.6中描述的「struct hack」。 Dennis Ritchie稱之爲「C實施中的無端愚蠢」。 C99引入了一種新的語言特性,即「靈活的數組成員」,以替代struct hack。即使是微軟的編譯器,它缺乏C99支持,也支持靈活的陣列成員。 – 2012-09-11 18:37:11

+0

請勿將`c`標籤添加到此問題中。C++規則與C規則完全不同。 – 2014-05-21 06:03:52

+0

@BenVoigt接受的答案是純粹的C代碼,所以我想你的編輯是錯誤的。 c hack以同樣的方式適用於c和C++ – 2014-05-21 06:14:23

回答

33

是的,這是一個C-Hack。
要創建任何長度的數組:

struct someData* mallocSomeData(int size) 
{ 
    struct someData* result = (struct someData*)malloc(sizeof(struct someData) + size * sizeof(BYTE)); 
    if (result) 
    { result->nData = size; 
    } 
    return result; 
} 

現在你有someData的目的具有指定長度的數組。

22

這是一箇舊的C劈死允許靈活大小的數組。

在C99標準中,這不是必需的,因爲它支持arr []語法。

+3

不幸的是,當談到C99支持時,Visual Studio很差。 :( – 2008-11-17 07:04:50

23

不幸的是,有幾個原因會導致您在結構的末尾聲明零長度的數組。它基本上賦予你從API返回的可變長度結構的能力。

Raymond Chen在這個問題上做了一個很好的博客文章。我建議你看看這篇文章,因爲它可能包含你想要的答案。

注意在他的文章中,它處理的是大小爲1而不是爲0的數組。 這種情況是因爲零長度數組是最近進入標準的情況。 他的帖子應該仍然適用於您的問題。

http://blogs.msdn.com/oldnewthing/archive/2004/08/26/220873.aspx

編輯

注意:雖然雷蒙德的帖子中寫道:0長度數組是合法的C99他們實際上還是不合法的C99。而不是0長度的陣列在這裏,你應該使用長度爲1的陣列

8

你的關於「爲什麼不使用大小爲1的數組」的說法是現貨。

該代碼正在執行「C struct hack」錯誤,因爲零長度數組的聲明是違反約束的。這意味着編譯器可以在編譯時立即拒絕你的黑客攻擊,並帶有停止翻譯的診斷消息。

如果我們想進行黑客攻擊,我們必須偷偷通過編譯器。

做了「C結構黑客」(這是一個用C方言追溯到1989 ANSI C,而且可能更早兼容)是用大小爲1的完全有效的陣列正確的做法:

struct someData 
{ 
    int nData; 
    unsigned char byData[1]; 
} 

此外,代替sizeof struct someData,零件的byData之前的大小,使用計算:

offsetof(struct someData, byData); 

byData與空間分配struct someData爲42個字節,我們再使用:

struct someData *psd = (struct someData *) malloc(offsetof(struct someData, byData) + 42); 

請注意,即使在數組大小爲零的情況下,該計算實際上也是正確的計算。你看,sizeof整個結構可以包含填充。舉例來說,如果我們有這樣的事情:

struct hack { 
    unsigned long ul; 
    char c; 
    char foo[0]; /* assuming our compiler accepts this nonsense */ 
}; 

struct hack的大小是因爲ul成員的排列很可能填充。如果unsigned long是四個字節寬,那麼很可能是sizeof (struct hack)是8,而offsetof(struct hack, foo)幾乎肯定是5. offsetof方法是在數組之前獲得結構的前一部分的準確大小的方式。

因此,這將是重構代碼的方式:使其符合經典,高度可移植的結構破解。

爲什麼不使用指針?因爲指針佔用額外的空間並且必須被初始化。

還有其他很好的理由不使用指針,即指針需要地址空間纔能有意義。 struct hack是可以外部化的:也就是說,在某些情況下,這樣的佈局符合外部存儲,比如文件,數據包或共享內存區域,因爲它們沒有意義,所以不需要指針。

幾年前,我在內核和用戶空間之間的共享內存消息傳遞接口中使用了struct hack。我不想要指針,因爲它們只對生成消息的進程的原始地址空間有意義。軟件的內核部分使用其自己的映射在不同地址的內存視圖,因此所有內容都基於偏移量計算。

0

這是值得指出IMO做尺寸計算的最佳方法,在上面鏈接的雷蒙德陳文章中使用。

struct foo 
{ 
    size_t count; 
    int data[1]; 
} 

size_t foo_size_from_count(size_t count) 
{ 
    return offsetof(foo, data[count]); 
} 

第一個條目偏離期望分配結束的位置也是所需分配的大小。海事組織這是一個非常優雅的方式來做尺寸計算。不重要的是可變大小數組的元素類型是什麼。 offsetof(或者Windows中的FIELD_OFFSET或UFIELD_OFFSET)總是以相同的方式寫入。沒有sizeof()表達式意外地搞亂了。