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++ 0x)はまだ開発中であり、柔軟な配列メンバーが追加されていないようです。
C++は、空のインデックス表記または0
インデックス表記(ベンダー固有の拡張を除く)を使用して、構造の最後にあるC99フレキシブル配列メンバーをサポートしていません。
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];
};
物事は有効であり、非常にうまく機能します。 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の記事では、「柔軟な」配列を初期化する例にタイプミス/バグがあります。それは読むべきです:
for (DWORD Index = 0; Index < NumberOfGroups; Index++) { // note: used '<' , not '='
TokenGroups->Groups[Index] = ...;
}
2つ目は要素を含まず、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
sの配列として動作しますが、配列内のデータは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];
};
提案が進行中であり、将来のC++バージョンになる可能性があります。詳細は https://thephd.github.io/vendor/future_cxx/papers/d1039.html を参照してください(この提案はかなり新しいため、変更される可能性があります)
あなただけが欲しいなら
struct blah { int foo[]; };
その後、構造体はまったく必要ありません。malloc'ed/ new'ed int arrayを処理するだけで済みます。
最初にメンバーがいる場合:
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++コードから使用できる柔軟な配列メンバーを宣言するために同じ問題に直面しました。 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
バージョンに応じて、条件付きマクロをもう1つ追加する必要があります。