Linuxカーネルコードで、理解できない次のことがわかりました。
struct bts_action {
u16 type;
u16 size;
u8 data[0];
} __attribute__ ((packed));
コードはこちら: http://lxr.free-electrons.com/source/include/linux/ti_wilink_st.h
要素がゼロのデータ配列の必要性と目的は何ですか?
Linuxカーネルコードで、理解できない次のことがわかりました。
struct bts_action {
u16 type;
u16 size;
u8 data[0];
} __attribute__ ((packed));
コードはこちら: http://lxr.free-electrons.com/source/include/linux/ti_wilink_st.h
要素がゼロのデータ配列の必要性と目的は何ですか?
長さゼロの配列のもう 1 つの使用法は、コンパイル時の構造体オフセット チェックを支援するための構造体内の名前付きラベルとしてです。
いくつかの大きな構造体定義 (複数のキャッシュ ラインにまたがる) があり、それらが境界と交差する最初と中間の両方で、それらがキャッシュ ライン境界に揃えられるようにしたいとします。
struct example_large_s
{
u32 first; // align to CL
u32 data;
....
u64 *second; // align to second CL after the first one
....
};
コードでは、次のような GCC 拡張機能を使用して宣言できます。
__attribute__((aligned(CACHE_LINE_BYTES)))
ただし、これが実行時に強制されることを確認する必要があります。
ASSERT (offsetof (example_large_s, first) == 0);
ASSERT (offsetof (example_large_s, second) == CACHE_LINE_BYTES);
これは単一の構造体に対しては機能しますが、多くの構造体をカバーするのは難しく、それぞれに異なるメンバー名が配置されています。各構造体の最初のメンバーの名前を見つける必要がある以下のようなコードを取得する可能性が最も高いでしょう。
assert (offsetof (one_struct, <name_of_first_member>) == 0);
assert (offsetof (one_struct, <name_of_second_member>) == CACHE_LINE_BYTES);
assert (offsetof (another_struct, <name_of_first_member>) == 0);
assert (offsetof (another_struct, <name_of_second_member>) == CACHE_LINE_BYTES);
このようにする代わりに、一貫した名前を持つ名前付きラベルとして機能する構造体で長さゼロの配列を宣言できますが、スペースは消費しません。
#define CACHE_LINE_ALIGN_MARK(mark) u8 mark[0] __attribute__((aligned(CACHE_LINE_BYTES)))
struct example_large_s
{
CACHE_LINE_ALIGN_MARK (cacheline0);
u32 first; // align to CL
u32 data;
....
CACHE_LINE_ALIGN_MARK (cacheline1);
u64 *second; // align to second CL after the first one
....
};
そうすれば、ランタイム アサーション コードの保守がはるかに簡単になります。
assert (offsetof (one_struct, cacheline0) == 0);
assert (offsetof (one_struct, cacheline1) == CACHE_LINE_BYTES);
assert (offsetof (another_struct, cacheline0) == 0);
assert (offsetof (another_struct, cacheline1) == CACHE_LINE_BYTES);