web-dev-qa-db-ja.com

可変長配列型の構造体要素を持つことはできますか?

可変長の構造要素を宣言できますか?

状態は次のとおりです。

typedef struct
{
   uint8_t No_Of_Employees;
   uint8_t Employee_Names[No_Of_Employees][15];
}st_employees;
10
user12345

C99 またはC11でコーディングする場合は、 フレキシブル配列メンバー を使用することをお勧めします(明示的な次元は指定しませんが、実行時に次のように規則を設定する必要があります)あなたの頭)。

_ typedef struct {
    unsigned No_Of_Employees;
    char* Employee_Names[]; // conventionally with No_of_Employees slots
 }st_employees;
_

どのアレイでも、フレキシブルアレイメンバーの各スロットのサイズは固定されています。ポインタを使用しています(たとえば、Linux/x86-64マシンでは8バイト)。

(C99標準以前の古いコンパイラでは、標準に反する場合でも_0_のような_char* Employee_Names[0];_ディメンションを指定しようとする場合があります)

次に、このような構造を、たとえば.

_ st_employees* make_employees(unsigned n) {
    st_employees* s = malloc(sizeof(s_employees)+n*sizeof(char*));
    if (!s) { perror("malloc make_employees"); exit(EXIT_FAILURE); };
    s->No_of_Employees = n;
    for (unsigned i=0; i<n; i++) s->Employe_Names[i] = NULL;
    return s;
 }
_

そしてあなたは( strdup(3) ヒープ内の文字列を複製して)それを好きなように使うかもしれません

_ st_employees* p = make_employees(3);
 p->Employee_Names[0] = strdup("John");
 p->Employee_Names[1] = strdup("Elizabeth");
 p->Employee_Names[2] = strdup("Brian Kernighan");
_

void destroy_employee(st_employee*e)関数が必要です(読者への演習として残しました)。おそらく、iからfreeまでループして__e->Employee_Names[i]_ごとにループし、次にfree(e);...

メモリの使用に関する規則を文書化することを忘れないでください(mallocfreeの呼び出しを担当する人)。 C動的メモリ割り当て (および メモリの断片化 および バッファオーバーフロー およびその他の 未定義の動作 を怖がる)の詳細を読む)。

[〜#〜] gcc [〜#〜]GCC 5 より古い場合は、古いGCC 4のデフォルトの標準なので、必ず_gcc -std=c99 -Wall_でコンパイルしてください。コンパイラはC89です。新しいコンパイラーの場合は、すべての警告とそれらの詳細を尋ねます。 _gcc -Wall -Wextra_...

14

TL; DR回答-いいえ、できません。

詳しく説明すると、C11、§6.7.2.1、Structure and union specifiersemphasis mine)を引用させてください。

構造体または共用体のメンバーは、可変的に変更された型以外の完全なオブジェクト型を持つことができます。[...]

そして、 [〜#〜] vla [〜#〜] は可変的に変更されるタイプです。

ただし、 フレキシブル配列メンバー に関して、同じ標準からの引用

特殊なケースとして、複数の名前付きメンバーを持つ構造体の最後の要素は、不完全な配列型になる場合があります。これはフレキシブル配列メンバーと呼ばれます。 [...]

したがって、次のようなことができます

typedef struct
{
   uint8_t No_Of_Employees;
   uint8_t* Employee_Names[];
}st_employees;

その後、ランタイムで動的にEmployee_Names(およびEmployee_Names[i]も)にメモリを割り当て、それを利用できます。

5
Sourav Ghosh

私の理解では、これは不可能です。構造体の1つのフィールドを別のフィールドで定義することはできません。

1
Codor

番号、

構造体を定義するときは、そのサイズを確認する必要があります。そのため、その構造体型の変数を宣言すると、その変数にメモリを割り当てることができます。

このシナリオについて考えてみましょう。 st_employees型の変数pを宣言する場合、No_Of_Employeesはまだ設定されていないため、変数pのサイズは確認されていません。変数のメモリを割り当てることができません。ただし、No_Of_Employees型の変数を宣言せずにst_employeesを設定することはできません。そのパラドックス。

1
Haris

これは、次のようにダイナミックアロケーションで実行できます。

#include <stdio.h>
#include <stdlib.h>

#define SIZE_OF_ELEM 15
#define uint8_t char 
typedef struct
{
   uint8_t No_Of_Employees;
   uint8_t **Employee_Names;
}st_employees;

int main()
{
    int i;
    st_employees emps;
    emps.No_Of_Employees = 2; //number of elements
    // allocate the number of elements
    emps.Employee_Names = malloc(emps.No_Of_Employees);
    for (i=0; i < emps.No_Of_Employees; i++)
    {
        // allocate each element
        emps.Employee_Names[i] = malloc(SIZE_OF_ELEM);
        // fill the element with some data
        sprintf(emps.Employee_Names[i], "emp_n%d", i);
    }
    // show the content
    for (i=0; i<emps.No_Of_Employees; i++)
    {
        printf("Employee %d content: %s\n", i, emps.Employee_Names[i]);
    }
    return 0;
}

もちろんこれは説明図です。割り当てをチェックし、typeoftypeを正確にチェックし、メモリを解放する必要があります。

このメソッドを使用すると、さまざまなタイプのオブジェクトのコレクションを作成でき、特定のCコンパイラのバージョンやオプションを使用する必要がないことに注意してください。

ただし、非常に基本的なケースでは(OPの例のように)、メモリを断片化するため(オブジェクトごとに1つの割り当て)、より良い解決策ではありません。したがって、これは注意して使用してください。

0
daouzli