4

私は構造体を広範囲に使用してきましたが、特に値が構造体へのポインターである場所*valueの代わりに、最初のメンバーは安全ですか?value->first_valuefirst_value*value

また、アラインメントのためにサイズが保証されていないことに注意してください。アーキテクチャ/レジスタのサイズに基づくアラインメント値は何ですか?

より高速な実行のためにデータ/コードを調整します。コンパイラにこれを行わないように指示できますか? サイズなど、構造体に関する特定のことを保証できるのではないでしょうか?

メンバーのオフセットを見つけるために構造体メンバーでポインター演算を行う-場合+、ビッグ エンディアンの場合はリトル エンディアンの場合と考えますか、それともコンパイラに依存するだけですか?

malloc(0) は実際に何を割り当てますか?

次のコードは、教育/発見を目的としたものであり、製品品質を意図したものではありません。

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

int main()
{
    printf("sizeof(struct {}) == %lu;\n", sizeof(struct {}));
    printf("sizeof(struct {int a}) == %lu;\n", sizeof(struct {int a;}));
    printf("sizeof(struct {int a; double b;}) == %lu;\n", sizeof(struct {int a; double b;}));
    printf("sizeof(struct {char c; double a; double b;}) == %lu;\n", sizeof(struct {char c; double a; double b;}));

    printf("malloc(0)) returns %p\n", malloc(0));
    printf("malloc(sizeof(struct {})) returns %p\n", malloc(sizeof(struct {})));

    struct {int a; double b;} *test = malloc(sizeof(struct {int a; double b;}));
    test->a = 10;
    test->b = 12.2;
    printf("test->a == %i, *test == %i \n", test->a, *(int *)test);
    printf("test->b == %f, offset of b is %i, *(test - offset_of_b) == %f\n",
        test->b, (int)((void *)test - (void *)&test->b),
        *(double *)((void *)test - ((void *)test - (void *)&test->b))); // find the offset of b, add it to the base,$

    free(test);
    return 0;
}

呼び出しgcc test.cに続いて、./a.out 私はこれを取得します:

sizeof(struct {}) == 0;
sizeof(struct {int a}) == 4;
sizeof(struct {int a; double b;}) == 16;
sizeof(struct {char c; double a; double b;}) == 24;
malloc(0)) returns 0x100100080
malloc(sizeof(struct {})) returns 0x100100090
test->a == 10, *test == 10 
test->b == 12.200000, offset of b is -8, *(test - offset_of_b) == 12.200000

更新 これは私のマシンです:

gcc --version

i686-apple-darwin10-gcc-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5666) (dot 3)
Copyright (C) 2007 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

uname -a

Darwin MacBookPro 10.8.0 Darwin Kernel Version 10.8.0: Tue Jun  7 16:33:36 PDT 2011; root:xnu-1504.15.3~1/RELEASE_I386 i386
4

5 に答える 5

6

6.2.5/20 以降:

構造体型は、順番に割り当てられたメンバー オブジェクト (および、特定の状況では不完全な配列) の空でないセットを記述します。各オブジェクトには、オプションで指定された名前と、場合によっては異なる型があります。

答える:

特に、value->first_value の代わりに *value を使用します。ここで、value は構造体へのポインターであり、first_value は最初のメンバーです。*value は安全ですか?

6.7.2.1/15 を参照:

15 構造体オブジェクト内で、ビットフィールド以外のメンバーとビットフィールドが存在するユニットには、宣言された順序で増加するアドレスがあります。適切に変換された構造体オブジェクトへのポインターは、その最初のメンバー (または、そのメンバーがビットフィールドの場合は、それが存在するユニット) を指し、その逆も同様です。構造体オブジェクト内に名前のないパディングがある場合がありますが、先頭にはありません。1

ただし、構造体の最後にメンバー間のパディング バイトがある場合もあります。

C では、malloc( 0 )実装定義です。(ちなみに、これは C と C++ が異なる小さなことの 1 つです。)

[1] 地雷を強調します。

于 2012-06-15T19:46:21.843 に答える
3

呼び出すと、少なくとも 1 回はmalloc(0)安全に渡すことができるポインタが返されます。free()複数の呼び出しで同じ値が返された場合、malloc(0)そのような呼び出しごとに 1 回解放される場合があります。明らかに、 が返された場合、何回NULL渡されても効果はありません。null 以外を返すfree()へのすべての呼び出しは、返された値を使用して への呼び出しによってバランスを取る必要があります。malloc(0)free()

于 2012-06-15T19:47:35.090 に答える
3

私は構造体を広範囲に使用してきましたが、特に構造体へのポインターがどこにあるのか、最初のメンバーは安全なのか、興味深いことを見*valueてきvalue->first_valueました。valuefirst_value*value

はい、*value安全です。指す構造のコピーを生成しますvalue。しかし、 とは異なる型を持つことがほぼ保証されている*value->first_valueため、 の結果*valueはほぼ常に とは異なり*value->first_valueます。


反例:

struct something { struct something *first_value; ... };
struct something data = { ... };
struct something *value = &data;
value->first_value = value;

このかなり限られた状況では、 と から同じ結果が得られ*valueます*value->first_value。そのスキームでは、型は同じになります (値が異なる場合でも)。一般に、 と の型*value*value->first_value異なる型です。


また、アラインメントのためにサイズが保証されていないことに注意してください。ただし、アラインメントは常にレジスタのサイズですか?

「レジスタサイズ」は定義された C の概念ではないため、何を求めているのか明確ではありません。プラグマ (#pragma packまたは同様のもの) がない場合、構造体の要素は、値が読み取られる (または書き込まれる) ときに最適なパフォーマンスが得られるように整列されます。

より高速な実行のためにデータ/コードを調整します。これを行わないようにコンパイラに指示できますか? では、サイズなど、構造体に関する特定のことを保証できるのでしょうか?

コンパイラは、型のサイズとレイアウトを担当しstructます。慎重な設計によって影響を与えることができます#pragma pack

これらの問題は通常、人々がデータのシリアル化について懸念している場合 (または、構造要素を 1 つずつ処理してデータをシリアル化する必要を回避しようとしている場合) に発生します。一般的に、シリアル化を行う関数を作成し、コンポーネントの断片から構築する方がよいと思います。

メンバ オフセットを見つけるために構造体メンバでポインタ演算を行う場合、リトル エンディアンの場合は減算、ビッグ エンディアンの場合は加算、または単にコンパイラに依存していると思いますか?

structおそらく、メンバーに対してポインター演算を行わない方がよいでしょう。必要に応じて、offsetof()マクロ from<stddef.h>を使用してオフセットを正しく処理してください (つまり、ポインター演算を直接行っていないことを意味します)。最初の構造体要素は、ビッグ エンディアンまたはリトル エンディアンに関係なく、常に最下位アドレスにあります。実際、エンディアンは、構造内のさまざまなメンバーのレイアウトには関係ありません。構造体の (基本データ型) メンバー内の値のバイト順序にのみ影響します。

C 標準では、構造体の要素を定義された順序で配置する必要があります。最初の要素は最下位のアドレスにあり、次の要素は上位のアドレスにあり、各要素について同様です。コンパイラは順序を変更できません。構造体の最初の要素の前にパディングを入れることはできません。構造体の任意の要素の後にパディングが存在する可能性があります。これは、適切なアラインメントと見なされるものをコンパイラが適切と見なすためです。構造体のサイズは、適切にアラインされた (N × サイズ) バイトを割り当て (たとえば を介してmalloc())、結果を構造体の配列として扱うことができるようなサイズです。

于 2012-06-15T19:55:21.953 に答える
2

内部構造がある場合、それが外側の構造の最初の宣言である場合、外側の構造と同じアドレスで始まることが保証されます。

したがって*valuevalue->first次のように、同じアドレスで (ただし異なる型を使用して) メモリにアクセスしています。

struct St {
  long first;
} *value;

また、構造体のメンバー間の順序は、宣言順序と同じであることが保証されています

アラインメントを調整するには、コンパイラ固有のディレクティブを使用するか、ビットフィールドを使用できます。

構造体メンバーの配置は、通常、ターゲット プラットフォームで個々のメンバーにアクセスするのに最適なものに基づいています。

また、 の場合malloc、返されたアドレスの近くに簿記を保持する可能性があるため、サイズがゼロのメモリでも有効なアドレスを返すことができます (返されたアドレスを介して何かにアクセスしようとしないでください)。

于 2012-06-15T19:50:08.553 に答える
0

構造体のサイズがどのように機能するかについて学ぶことが重要です。例えば:

struct foo{
  int i;
  char c;
}

struct bar{
  int i;
  int j;
}

struct baz{
  int i;
  char c;
  int j;
}

sizeof(foo) = 8 bytes (32 bit arch)
sizeof(bar) = 8 bytes
sizeof(baz) = 12 bytes

これが意味することは、構造体のサイズとオフセットは次の 2 つの規則に従う必要があるということです。

1- 構造体は最初の要素の倍数でなければなりません (なぜ foo は 5 バイトではなく 8 なのですか)

2-構造体要素は、それ自体の倍数で開始する必要があります。(baz では、int j は 6 で開始できなかったため、バイト 6、7、および 8 は無駄なパディングです。

于 2012-06-15T19:56:05.520 に答える