web-dev-qa-db-ja.com

柔軟な配列メンバーで構造を初期化する方法

私は次の構造を持っています

_typedef struct _person {
    int age;
    char sex;
    char name[];
}person;
_

malloc()を使用せずに、インスタンスを作成し、柔軟な配列メンバーを使用して構造を初期化する方法について、いくつかの基本的なインターネット検索を実行しました(ただし失敗しました)。

例:次のような通常の構造の場合

_struct a {
    int age; 
    int sex;
};
_

_struct a_のインスタンスを作成し、次のように初期化できます。

_struct a p1 = {10, 'm'};
_

しかし、柔軟な配列を含む構造(上記の__person_など)の場合、通常のstructuresの場合と同じように、インスタンスを作成して初期化するにはどうすればよいですか?

それも可能ですか?もしそうなら、初期化中に配列サイズと初期化される実際の値をどのように渡すのですか?

(または)

柔軟な配列を持つ構造を作成する唯一の方法は、C99仕様に記載されているようにmalloc()を使用することです--_6.7.2.1 Structure and union specifiers - point #17_?!

22

いいえ、柔軟な配列は常に手動で割り当てる必要があります。ただし、callocを使用してフレキシブル部分を初期化し、複合リテラルを使用して固定部分を初期化することができます。これを次のような割り当てinline関数でラップします。

typedef struct person {
  unsigned age;
  char sex;
  size_t size;
  char name[];
} person;

inline
person* alloc_person(int a, char s, size_t n) {
  person * ret = calloc(sizeof(person) + n, 1);
  if (ret) memcpy(ret,
                  &(person const){ .age = a, .sex = s, .size = n},
                  sizeof(person));
  return ret;
}

これにより、割り当てが呼び出し元に成功したかどうかのチェックが終了することに注意してください。

ここに含めたようにsizeフィールドが必要ない場合は、マクロで十分です。 callocを実行する前にmemcpyの戻り値を確認できないことだけです。これまでにプログラムしたすべてのシステムで、これは比較的うまく中止されます。一般的には mallocの返還はあまり重要ではないと思います ですが、意見はそのテーマによって大きく異なります。

これはおそらく(その特別な場合に)オプティマイザーにコードを周囲に統合する機会を与える可能性があります。

#define ALLOC_PERSON(A,  S,  N)                                 \
((person*)memcpy(calloc(sizeof(person) + (N), 1),               \
                 &(person const){ .age = (A), .sex = (S) },     \
                 sizeof(person)))

編集:これが関数よりも優れている可能性があるのは、ASがコンパイル時定数の場合です。その場合、複合リテラルはconst修飾されているため、静的に割り当てることができ、その初期化はコンパイル時に実行できます。さらに、同じ値を持つ複数の割り当てがコードに表示される場合、コンパイラはその複合リテラルの単一のコピーのみを実現できます。

10
Jens Gustedt

使用できるトリックがいくつかあります。それはあなたの特定のアプリケーションに依存します。

単一の変数を初期化する場合は、正しいサイズの構造を定義できます。

   struct  {
        int age;
        char sex;
        char name[sizeof("THE_NAME")];
    } your_variable = { 55, 'M', "THE_NAME" };

問題は、変数を「person」として解釈するためにポインタキャストを使用する必要があることです(例:「*(person *)(&your_variable)」。ただし、これを回避するには、包含ユニオンを使用できます。

union {
 struct { ..., char name[sizeof("THE_NAME")]; } x;
 person p;
} your_var = { 55, 'M', "THE_NAME" };

したがって、your_var.pのタイプは「person」です。マクロを使用して初期化子を定義することもできるため、文字列を書き込むことができるのは1回だけです。

#define INIVAR(x_, age_, sex_ ,s_) \
   union {\
     struct { ..., char name[sizeof(s_)]; } x;\
     person p;\
    } x_ = { (age_), (sex_), (s_) }

INIVAR(your_var, 55, 'M', "THE NAME");

もう1つの問題は、このトリックが「人」の配列を作成するのに適していないことです。配列の問題は、すべての要素が同じサイズでなければならないことです。この場合、const char *の代わりにchar[]を使用する方が安全です。または、動的割り当てを使用します;)

5

フレキシブル配列メンバーを持つ構造体タイプは、フレキシブル配列メンバーが省略されているかのように扱うことができるため、このように構造体を初期化できます。

person p = { 10, 'x' };

ただし、柔軟な配列のメンバーは割り当てられておらず、柔軟な配列のメンバーにアクセスしたり、その終わりを超えてメンバーへのポインターを形成したりする試みは無効です。この配列に実際に要素を持つ柔軟な配列メンバーを持つ構造体のインスタンスを作成する唯一の方法は、たとえばmallocを使用して動的にメモリを割り当てることです。

3
CB Bailey