[英]Are flexible array members valid in C++?
在 C99 中,您可以像這樣聲明結構的靈活數組成員:
struct blah
{
int foo[];
};
但是,當工作中的某個人嘗試在 C++ 中使用 clang 編譯一些代碼時,該語法不起作用。 (它一直在與 MSVC 一起工作。)我們必須將其轉換為:
struct blah
{
int foo[0];
};
查看 C++ 標准,我發現根本沒有提到靈活的成員數組; 我一直認為[0]
是無效聲明,但顯然對於靈活的成員數組它是有效的。 靈活的成員數組在 C++ 中實際上有效嗎? 如果是這樣,正確的聲明是[]
還是[0]
?
C++ 於 1998 年首次標准化,因此它早於向 C(C99 中的新成員)添加靈活數組成員。 2003 年對 C++ 進行了更正,但沒有添加任何相關的新功能。 C++ (C++2b) 的下一個修訂版仍在開發中,似乎還沒有添加靈活的數組成員。
C++ 不支持結構末尾的 C99 靈活數組成員,無論是使用空索引符號還是0
索引符號(除非供應商特定的擴展):
struct blah
{
int count;
int foo[]; // not valid C++
};
struct blah
{
int count;
int foo[0]; // also not valid C++
};
據我所知,C++0x 也不會添加這個。
但是,如果將數組大小調整為 1 個元素:
struct blah
{
int count;
int foo[1];
};
代碼將編譯,並且運行良好,但它在技術上是未定義的行為。 您可以使用不太可能出現非一錯誤的表達式分配適當的內存:
struct blah* p = (struct blah*) malloc( offsetof(struct blah, foo[desired_number_of_elements]);
if (p) {
p->count = desired_number_of_elements;
// initialize your p->foo[] array however appropriate - it has `count`
// elements (indexable from 0 to count-1)
}
因此它可以在 C90、C99 和 C++ 之間移植,並且與 C99 的靈活數組成員一樣有效。
Raymond Chen 對此寫了一篇很好的文章:為什么有些結構以大小為 1 的數組結尾?
注意:在 Raymond Chen 的文章中,初始化“flexible”數組的示例中存在拼寫錯誤/錯誤。 它應該是:
for (DWORD Index = 0; Index < NumberOfGroups; Index++) { // note: used '<' , not '='
TokenGroups->Groups[Index] = ...;
}
第二個將不包含元素,而是在blah
之后立即指向。 所以如果你有這樣的結構:
struct something
{
int a, b;
int c[0];
};
你可以做這樣的事情:
struct something *val = (struct something *)malloc(sizeof(struct something) + 5 * sizeof(int));
val->a = 1;
val->b = 2;
val->c[0] = 3;
在這種情況下, c
將表現為具有 5 個int
的數組,但數組中的數據將在something
結構之后。
我正在開發的產品使用它作為一個大小的字符串:
struct String
{
unsigned int allocated;
unsigned int size;
char data[0];
};
由於支持的體系結構,這將消耗 8 個字節加上allocated
.
當然,所有這些都是 C 語言,但例如 g++ 可以毫無障礙地接受它。
如果您可以將您的應用程序限制為僅需要幾個已知大小,那么您可以使用模板有效地實現靈活的數組。
template <typename BASE, typename T, unsigned SZ>
struct Flex : public BASE {
T flex_[SZ];
};
如果你只想
struct blah { int foo[]; };
那么你根本不需要結構體,你可以簡單地處理一個 malloc'ed/new'ed int 數組。
如果一開始有一些成員:
struct blah { char a,b; /*int foo[]; //not valid in C++*/ };
然后在 C++ 中,我想你可以用foo
成員函數替換foo
:
struct blah { alignas(int) char a,b;
int *foo(void) { return reinterpret_cast<int*>(&this[1]); } };
使用示例:
#include <stdlib.h>
struct blah {
alignas(int) char a,b;
int *foo(void) { return reinterpret_cast<int*>(&this[1]); }
};
int main()
{
blah *b = (blah*)malloc(sizeof(blah)+10*sizeof(int));
if(!b) return 1;
b->foo()[1]=1;
}
一項提案正在進行中,可能會成為未來的 C++ 版本。 有關詳細信息,請參閱http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1039r0.html (該提案相當新,因此可能會發生變化)
我面臨同樣的問題來聲明一個可以從 C++ 代碼中使用的靈活數組成員。 通過查看glibc
頭文件,我發現有一些靈活數組成員的用法,例如在struct inotify
中聲明如下(省略了注釋和一些不相關的成員):
struct inotify_event
{
//Some members
char name __flexarr;
};
__flexarr
宏又被定義為
/* Support for flexible arrays.
Headers that should use flexible arrays only if they're "real"
(e.g. only if they won't affect sizeof()) should test
#if __glibc_c99_flexarr_available. */
#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L
# define __flexarr []
# define __glibc_c99_flexarr_available 1
#elif __GNUC_PREREQ (2,97)
/* GCC 2.97 supports C99 flexible array members as an extension,
even when in C89 mode or compiling C++ (any version). */
# define __flexarr []
# define __glibc_c99_flexarr_available 1
#elif defined __GNUC__
/* Pre-2.97 GCC did not support C99 flexible arrays but did have
an equivalent extension with slightly different notation. */
# define __flexarr [0]
# define __glibc_c99_flexarr_available 1
#else
/* Some other non-C99 compiler. Approximate with [1]. */
# define __flexarr [1]
# define __glibc_c99_flexarr_available 0
#endif
我不熟悉MSVC
編譯器,但可能您必須根據MSVC
版本再添加一個條件宏。
標准 C++ 不支持靈活的數組成員,但是 clang 文檔說。
“除了此處列出的語言擴展之外,Clang 還旨在支持廣泛的 GCC 擴展。”
C++ 的 gcc 文檔說。
“GNU 編譯器為 C++ 語言提供了這些擴展(您也可以在 C++ 程序中使用大多數 C 語言擴展)。”
C 文檔的 gcc 文檔支持零長度數組。
靈活數組還不是 C++ 標准的一部分。 這就是int foo[]
或int foo[0]
可能無法編譯的原因。 雖然有一個提案正在討論中,但它尚未被 C++ (C++2b) 的最新修訂版接受。
但是,幾乎所有現代編譯器都通過編譯器擴展支持它。
問題是,如果您以最高警告級別 ( -Wall --pedantic
) 使用此擴展程序,則可能會導致警告。
一種解決方法是使用一個包含一個元素的數組並進行越界訪問。 雖然這個解決方案是規范( dcl.array和expr.add )的UB,但大多數編譯器都會產生有效的代碼,甚至clang -fsanitize=undefined
對它很滿意:
#include <new>
#include <type_traits>
struct A {
int a[1];
};
int main()
{
using storage_type = std::aligned_storage_t<1024, alignof(A)>;
static storage_type memory;
A *ptr_a = new (&memory) A;
ptr_a->a[2] = 42;
return ptr_a->a[2];
}
更好的解決方案是將其聲明為指針:
struct blah
{
int* foo;
};
或者更好的是,將其聲明為std::vector
:
struct blah
{
std::vector<int> foo;
};
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.