1. ホーム
  2. c

[解決済み] 要素数0の配列の必要性とは?

2022-07-20 13:35:34

質問

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

要素が0個のデータの配列の必要性と目的は何ですか?

どのように解決するのですか?

これは、データのサイズを可変にするために、わざわざ malloc ( kmalloc を2回使用します。このように使うことになります。

struct bts_action *var = kmalloc(sizeof(*var) + extra, GFP_KERNEL);

これは以前は標準ではなく、(Aniket が言うように) ハックと見なされていましたが C99 で標準化されました。 . 今の標準的な書式は

struct bts_action {
     u16 type;
     u16 size;
     u8 data[];
} __attribute__ ((packed)); /* Note: the __attribute__ is irrelevant here */

のサイズについて何も言及していないことに注意してください。 data フィールドのサイズについて何も言及していないことに注意してください。また、この特別な変数は構造体の末尾にしかないことにも注意してください。


C99では、この件は6.7.2.1.16で説明されています(強調)。

特殊なケースとして、複数の名前付きメンバを持つ構造体の最後の要素は、不完全な配列型を持つことがあります。 は不完全な配列型を持っています。 これをフレキシブル配列メンバと呼びます . ほとんどの場合 は無視されます。特に,構造体のサイズは 構造体のサイズは,フレキシブル配列のメンバが省略された場合と同じですが, 後続のパディングは省略された場合よりも多くなる可能性があります. を省略した場合と同じです。ただし、.演算子(または->)が左オペランドを持つ場合、そのオペランドは (へのポインタ)であり、右オペランドがそのフレキシブル配列のメンバを指定するものであるとき を指定した場合,そのメンバを(同じ要素型を持つ)最長の配列に置き換えたように動作します. 構造体がアクセスされるオブジェクトより大きくならないような最長の配列(同じ要素タイプ)に置き換えたように動作する。 配列のオフセットは,柔軟な配列メンバのオフセットと異なる場合でも,そのオフセットのままでなければならない。 配列のオフセットは,たとえそれが置換配列のオフセットと異なっていても,柔軟な配列メンバのオフセットのままでなければならない。この配列が要素をもたない場合は,あたかも一つの要素をもつかのようにふるまう。 しかし,その要素にアクセスしようとしたり,その要素の一つ前のポインタを生成しようとしたりすると,その動作は不定になる。 要素にアクセスしようとしたり、過去のポインタを生成しようとした場合の動作は未定義です。

あるいは言い換えれば、持っている場合。

struct something
{
    /* other variables */
    char data[];
}

struct something *var = malloc(sizeof(*var) + extra);

にアクセスすることができます。 var->data にインデックスを付けて [0, extra) . ただし sizeof(struct something) は他の変数を考慮したサイズしか与えませんので、 つまりは data を0にします。


また、この規格が実際にどのように malloc がそのような構成であることに注目してください (6.7.2.1.17)。

struct s { int n; double d[]; };

int m = /* some value */;
struct s *p = malloc(sizeof (struct s) + sizeof (double [m]));

同じ場所の規格によるもう一つの興味深い注意点は、(強調)です。

malloc の呼び出しが成功したと仮定すると、p が指すオブジェクトは、ほとんどの目的のために、p が宣言されていたかのように振る舞います。

struct { int n; double d[m]; } *p;

(この等価性が崩れる状況もあります。特に メンバーdのオフセットは同じではないかもしれません。 ).