0

私は以下のコードを持っていますが、それらの違いは何ですか? 最初のものは、構造体のbuf要素のアドレスが構造体のアドレスよりも 4 大きく、2 番目のものはそうではありません。

初め

#include <stdio.h>

typedef struct A
{
    int i;
    char buf[];  //Here
}A;

int main()
{
    A *pa = malloc(sizeof(A));
    char *p = malloc(13);
    memcpy(p, "helloworld", 10);
    memcpy(pa->buf, p, 13);

    printf("%x %x %d %s\n", pa->buf, pa, (char *)pa->buf - (char *)pa, pa->buf);
}

2番

typedef struct A
{
    int i;
    char *buf; //Here
}A;
4

2 に答える 2

6

1 つ目は、C99 の「柔軟な配列メンバー」です。2 つ目は、C99 以降がない場合の信頼できるフォールバックです。

柔軟な配列メンバーを使用すると、配列に必要なスペースをメイン構造体と共に割り当てます。

A *pa = malloc(sizeof(A) + strlen(string) + 1);

pa->i = index;
strcpy(pa->buf, string);

...use pa...

free(pa);

メモリ割り当てに関する限り、bufメンバーにはサイズがありません (したがってsizeof(A) == sizeof(int)、配列の配置によるパディングの問題がない限り、たとえば、 の柔軟な配列がある場合double)。

別の方法では、2 つの割り当て (および 2 つのリリース) が必要になるか、セットアップに注意が必要です。

typedef struct A2
{
    int   i;
    char *buf;
} A2;

A2 *pa2 = malloc(sizeof(A2));
pa2->buff = strdup(string);

...use pa2...

free(pa2->buff);
free(pa2);

または:

A2 *pa2 = malloc(sizeof(A2) + strlen(string) + 1);
pa2->buff = (char *)pa2 + sizeof(A2);

...use pa2...

free(pa2);

A2 を使用すると、ポインターのサイズ (単一割り当て)、またはポインターのサイズと 2 番目のメモリ割り当て (二重割り当て) のオーバーヘッドのいずれかによって、より多くのメモリが必要になることに注意してください。

「構造体ハック」と呼ばれるものが使用されているのを時々見かけます。これは C99 標準よりも前のものであり、柔軟な配列メンバーによって廃止されました。このコードは次のようになります。

typedef struct A3
{
    int  i;
    char buf[1];
} A3;

A3 *pa3 = malloc(sizeof(A3) + strlen(string) + 1);
strcpy(pa3->buf, string);

これは柔軟な配列メンバーとほぼ同じですが、構造が大きくなります。この例では、ほとんどのマシンで、構造体のA3長さは 8 バイトになります ( の 4 バイトではなくA)。

GCC は長さゼロの配列をある程度サポートしています。配列次元が 0 の構造体ハックが表示される場合があります。これは、GCC を模倣していないコンパイラには移植できません。

言語標準によって移植可能であることが保証されていないため、「構造体ハック」と呼ばれます (宣言された配列の境界外にアクセスしているため)。ただし、経験的に、それは「常に機能して」おり、おそらく今後も機能し続けるでしょう。それにもかかわらず、構造体ハックよりも柔軟な配列メンバーを使用する必要があります。


ISO/IEC 9899:2011 §6.7.2.1 構造体および共用体指定子

¶3 構造体または共用体は、不完全型または関数型のメンバーを含んではなりません (したがって、構造体はそれ自体のインスタンスを含んではなりませんが、それ自体のインスタンスへのポインターを含んでもよい)。複数の名前付きメンバーが不完全な配列型を持つ場合があります。そのような構造体 (およびそのような構造体であるメンバーをおそらく再帰的に含む共用体) は、構造体のメンバーまたは配列の要素であってはなりません。

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

于 2012-12-10T16:02:23.880 に答える
1
struct A {
    int i;
    char buf[];
};

配列または配列へのポインター用のスペースを予約しません。これが言っているのは、配列は の本体に直接続き、次のようにA経由でアクセスできるということです。buf

struct A *a = malloc(sizeof(*a) + 6);
strcpy(a->buf, "hello");
assert(a->buf[0] == 'h');
assert(a->buf[5] == '\0';

a「hello」と nul ターミネータ用に、次の 6 バイトを予約したことに注意してください。

ポインター形式は間接参照を使用します (メモリーは連続している可能性がありますが、これは依存も要求もされません)。

struct B {
    int i;
    char *buf;
};

/* requiring two allocations: */
struct B *b1 = malloc(sizeof(*b1));
b1->buf = strdup("hello");

/* or some pointer arithmetic */
struct B *b2 = malloc(sizeof(*b2) + 6);
b2->buf = (char *)((&b2->buf)+1);

a2 番目は、整数と char 配列の間のポインターを除いて、上記と同じように配置されます。

于 2012-12-10T16:09:10.473 に答える