[英]What happens if I define a 0-size array in C/C++?
只是好奇,如果我定義一個零長度數組int array[0];
實際上會發生什么int array[0];
在代碼中? GCC 根本沒有抱怨。
示例程序
#include <stdio.h>
int main() {
int arr[0];
return 0;
}
澄清
我實際上是想弄清楚零長度數組是否以這種方式初始化,而不是像 Darhazer 評論中的可變長度那樣指向,是否被優化。
這是因為我必須將一些代碼發布到野外,所以我想弄清楚是否必須處理SIZE
定義為0
,這種情況發生在一些具有靜態定義的int array[SIZE];
我實際上很驚訝 GCC 沒有抱怨,這導致了我的問題。 從我收到的答案來看,我認為沒有警告主要是由於支持尚未使用新 [] 語法更新的舊代碼。
因為我主要想知道錯誤,所以我將 Lundin 的答案標記為正確(Nawaz 是第一個,但它並不完整)——其他人指出它實際用於尾墊結構,雖然相關,但不是這正是我要找的。
數組的大小不能為零。
ISO 9899:2011 6.7.6.2:
如果表達式是一個常量表達式,它的值應該大於零。
上面的文字對於普通數組(第 1 段)都是正確的。 對於 VLA(可變長度數組),如果表達式的值小於或等於 0(第 5 段),則行為未定義。 這是 C 標准中的規范性文本。 不允許編譯器以不同方式實現它。
gcc -std=c99 -pedantic
對非 VLA 情況發出警告。
按照標准,這是不允許的。
然而,目前 C 編譯器的做法是將這些聲明視為靈活數組成員 ( FAM )聲明:
C99 6.7.2.1, §16 :作為一種特殊情況,具有多個命名成員的結構的最后一個元素可能具有不完整的數組類型; 這稱為靈活數組成員。
FAM 的標准語法是:
struct Array {
size_t size;
int content[];
};
這個想法是你會這樣分配它:
void foo(size_t x) {
Array* array = malloc(sizeof(size_t) + x * sizeof(int));
array->size = x;
for (size_t i = 0; i != x; ++i) {
array->content[i] = 0;
}
}
您也可以靜態使用它(gcc 擴展名):
Array a = { 3, { 1, 2, 3 } };
這也稱為尾部填充結構(該術語早於 C99 標准的發布)或struct hack (感謝 Joe Wreschnig 指出)。
然而,這種語法直到最近才在 C99 中標准化(並且保證了效果)。 在需要恆定大小之前。
1
是便攜式的方式,雖然它很奇怪。0
更能表明意圖,但就標准而言是不合法的,並且被一些編譯器(包括 gcc)支持作為擴展。 然而,尾部填充實踐依賴於存儲可用的事實(小心malloc
),因此通常不適合堆棧使用。
在標准C和C ++,零大小的數組不允許..
如果您使用 GCC,請使用-pedantic
選項編譯它。 它會發出警告,說:
zero.c:3:6: warning: ISO C forbids zero-size array 'a' [-pedantic]
在 C++ 的情況下,它會給出類似的警告。
這是完全非法的,而且一直都是,但是很多編譯器都忽略了發出錯誤信號。 我不確定你為什么要這樣做。 我知道的一個用途是從布爾值觸發編譯時錯誤:
char someCondition[ condition ];
如果condition
為假,則會出現編譯時錯誤。 但是,因為編譯器確實允許這樣做,所以我已經開始使用:
char someCondition[ 2 * condition - 1 ];
這給出了 1 或 -1 的大小,而且我從未找到接受 -1 大小的編譯器。
零長度數組的另一個用途是制作可變長度對象(C99 之前)。 零長度數組不同於具有 [] 而沒有 0 的靈活數組。
引用自gcc 文檔:
零長度數組在 GNU C 中是允許的。它們作為結構的最后一個元素非常有用,它實際上是可變長度對象的標頭:
struct line { int length; char contents[0]; }; struct line *thisline = (struct line *) malloc (sizeof (struct line) + this_length); thisline->length = this_length;
在 ISO C99 中,您將使用靈活的數組成員,它在語法和語義上略有不同:
- 靈活的數組成員寫為沒有 0 的內容 []。
- 靈活的數組成員具有不完整的類型,因此可能不應用 sizeof 運算符。
一個真實的例子是kdbus.h (一個 Linux 內核模塊)中struct kdbus_item
零長度數組。
我要補充一點,關於這個論點,gcc 的在線文檔有一整頁。
一些引用:
GNU C 中允許零長度數組。
在 ISO C90 中,您必須為內容指定長度為 1
和
3.0 之前的 GCC 版本允許靜態初始化零長度數組,就好像它們是靈活的數組一樣。 除了那些有用的情況外,它還允許在會破壞以后數據的情況下進行初始化
所以你可以
int arr[0] = { 1 };
和繁榮:-)
如果允許,結構中的零大小數組聲明將很有用,並且如果語義是這樣的:(1)它們將強制對齊但不分配任何空間,以及(2)索引數組將被視為在結果指針將與結構位於同一內存塊中的情況。 任何 C 標准都不允許這種行為,但一些較舊的編譯器在它成為編譯器的標准之前允許它允許帶有空括號的不完整數組聲明。
通常使用大小為 1 的數組實現的 struct hack 是狡猾的,我認為沒有任何要求編譯器避免破壞它。 例如,我希望如果編譯器看到int a[1]
,它有權將a[i]
視為a[0]
。 如果有人試圖通過類似的方法解決 struct hack 的對齊問題
typedef struct { uint32_t size; uint8_t data[4]; // Use four, to avoid having padding throw off the size of the struct }
編譯器可能會變得聰明並假設數組大小確實是四:
; As written foo = myStruct->data[i]; ; As interpreted (assuming little-endian hardware) foo = ((*(uint32_t*)myStruct->data) >> (i << 3)) & 0xFF;
這種優化可能是合理的,特別是如果myStruct->data
可以在與myStruct->size
相同的操作中加載到寄存器中。 我不知道標准中的任何內容會禁止這種優化,盡管它當然會破壞任何可能期望訪問第四個元素之外的內容的代碼。
當然,按照標准,您不能擁有零大小的數組,但實際上每個最流行的編譯器都可以讓您這樣做。 所以我會試着解釋為什么它會很糟糕
#include <cstdio>
int main() {
struct A {
A() {
printf("A()\n");
}
~A() {
printf("~A()\n");
}
int empty[0];
};
A vals[3];
}
我就像一個人會期望這樣的輸出:
A()
A()
A()
~A()
~A()
~A()
Clang 打印了這個:
A()
~A()
GCC 打印:
A()
A()
A()
這很奇怪,所以如果可以的話,最好不要在 C++ 中使用空數組。
在GNU C 中還有一個擴展,它使您可以在 C 中創建零長度數組,但據我所知,結構中應該至少有一個成員,否則如果您使用 C++,您將得到上述非常奇怪的示例.
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.