-2

私が構造体を持っているとしましょう:

typedef struct {
    int number1;  /* dummy */
    int number2;  /* dummy */
    int number3;  /* dummy */
    char *name1;
    char name2[];
} Klass;

残りのコードは次のとおりです。

int main(int argc, char const *argv[])
{
    char *name1 = "this is a name";            /* 1st case */
    char name2[] = "this is also a name";      /* 2nd case */

    Klass k;
    k.number1 = 10;
    k.number2 = 20;
    k.number3 = 30;
    k.name1 = "this is my first name";         /* 3rd case */

    /* error: invalid use of flexible array member */
    k.name2 = "this is my second name";

    Klass *kp = (Klass*)malloc(sizeof(Klass));
    kp->number1 = 100;
    kp->number2 = 200;
    kp->number3 = 300;
    kp->name1 = "this is also my first name";  /* 4th case */

    /* error: invalid use of flexible array member */
    kp->name2 = "this is my second name";

    return 0;
}
  1. マークされたケースでメモリがどのように割り当てられるか(ヒープとスタック)を誰かが明確にできますか?
  2. メインブロックの最後でどのようにメモリを解放する必要がありますか?
  3. コンパイラが与える理由は何error: invalid use of flexible array memberですか?

編集 あなたが言っk.name = "this is my name";てスタックにいる場合、私がこのようkp->name = "this is also my name";に到達する方法を説明できますか?"this is my name"

Klass *kp;

int foo() {
    Klass k;
    k.number1 = 10;
    k.number2 = 20;
    k.number3 = 30;
    k.name = "this is my name";
    kp = &k;
} // k is destroyed now

int main(int argc, char const *argv[])
{
    kp = (Klass*)malloc(sizeof(Klass));
    foo();
    printf("%d\n", kp->number1); /* segfault */
    printf("%d\n", kp->number2); /* segfault */
    printf("%d\n", kp->number3); /* segfault */
    printf("%s\n", kp->name);    /* prints "this is my name" */
    return 0;
}
4

4 に答える 4

1

柔軟な長さの配列は、主にポインターで使用することを目的としています。

Klass *kp = malloc( sizeof(Klass) + 100 );

name2この100は、メンバーを介してアクセスできる追加のバイトです。通常、柔軟なパーツサイズを格納するために使用される構造のメンバーがあります。

このメモリを解放することは、通常の構造に使用するものと何ら変わりはありません。

コンパイラが文句を言う理由は、それがまだ配列(ポインタではない)であり、単純に配列を割り当てることができないためです。アクセスしているときは、追加の間接参照は導入されません(がポインタkp->name2の場合とは異なります)。name2

メモリ割り当てについて:

1)name1は、文字列リテラルが格納されているメモリ領域(ほとんどの場合、読み取り専用メモリ)へのポインタです(終了が0のchar配列を構成するバイト)。を指す文字列を変更しようとするとname1、未定義の動作が発生します(文字列リテラルを変更することはできません)。

2)name2は配列であり、スタック上にあります。その内容は自由に変更できます。この配列に文字列リテラルを配置する初期化コードは、プラットフォームやコンパイラによって異なる場合があります。memcpy文字列リテラルが読み取り専用メモリ領域(.rodata)にあり、単に'name2に変換される分解を見たことさえあります。

3)と4)は、違法な配列への割り当ての試みです。

于 2012-10-18T12:57:21.947 に答える
1
    1. 文字列のメモリは、すべての文字列定数が存在する静的領域にあります。name1その領域への単なるポインタです。
    2. メモリには自動ストレージがあります(「スタック上」)。
    3. ケース1と同じ。
  1. はい、あなたが電話したのでmalloc。への呼び出しによってすべてmallocをミラーリングする必要がありますfree

  2. ではKlass、メンバーname2柔軟な配列メンバーmallocであり、そのサイズはコンパイラにはわかりません(そして、そのために、呼び出しでメモリを割り当てていません)。また、Cで配列を直接コピーすることはできません。十分なメモリを割り当てていれば、memcpyまたはを使用できますstrcpy

于 2012-10-18T12:59:15.940 に答える
1
char *name1 = "this is a name";            /* 1st case */

これは、ポインタを割り当て、文字列(静的データとして保持される)を指すように設定するだけです。

char name2[] = "this is also a name";      /* 2nd case */

これは略してchar name2[sizeof(init_string)] = "this is also a name";、文字列に文字を格納するのに十分なスペースを割り当てます。

3番目のケース

char name2[];

スペースをまったく割り当てません!文字列を保存する場所がありません(strcpyとにかく使用してコピーする必要があります)。

4番目のケース

kp->name1 = "this is also my first name";  /* 4th case */ 

ケース1に似ています-name1静的テキストを指すように設定されたポインタです。

于 2012-10-18T12:59:32.583 に答える
0

自分でメモリを解放する必要があるのは、(m)何かを割り当てるか、そうする関数を呼び出すときだけです(strdup()、...)

1、3、および4の場合、スタックに文字列を割り当てている場合、関数を終了すると、これらの文字列は使用できなくなります。2番目のケースは少し注意が必要ですが、この文字列は実行可能ファイルにハードコーディングされており、変更できません(そうしようとすると、セグメンテーション違反が発生します)。

name2はポインタではなく配列であるため、name2を文字列にポインティングすることはできません。

于 2012-10-18T12:57:11.333 に答える