0

1 つの malloc() ブロックを使用して、さまざまな型の複数の構造体を格納しようとしています。メンバーは、デモンストレーションを目的とした架空のものです。完全な (C コンパイラーによって完全に認識され、定義されている) 完全に正当な C 構造体を構成することを除いて、それらの型、順序などについて仮定を立てることはできません。今回はこれで、次はあれです。私はコンセプトを追求しています。独自のミックスを作成しますが、考え方は同じでなければなりません - ヘッダー構造体には他の構造体へのポインターがあります。

typedef struct
{
    int    a;
} s1_t;

typedef struct
{
    int    b;
} s2_t;

typedef struct
{
    int    c;
} s3_t;

typedef struct
{
    s1_t*    s1;
    s2_t*    s2;
    s3_t*    s3;
} hdr_t;

int
main( int argc, char* argv[] )
{
    void*    mem = malloc( sizeof( hdr_t ) + 
                           sizeof( s1_t ) + 
                           sizeof( s2_t ) + 
                           sizeof( s3_t ) );

    hdr_t    hdr = ( hdr_t* )mem;

    hdr->s1 = (s1_t* ) ( hdr + sizeof( hdr ) );
    hdr->s2 = (s2_t* ) ( hdr->s1 + sizeof( s1_t ) );
    hdr->s3 = (s3_t* ) ( hdr->s2 + sizeof( s2_t ) );

    /* etc. */
}

上記の (素朴な) コードは、小さな malloc でヒープを断片化するのではなく、(キーボード時に認識される) セット全体に対して 1 つのブロックを取得することを望んでいます。

質問: 可能な限りポータブルなソリューションを得るには、スキーム内の構造体を手動で整列する必要がありますか?

つまり、(疑似コード)、ALSZ = このチップのアライメント サイズ:

mem = malloc( hdrsz + ALSZ + s1sz + ALSZ + s2sz + ALSZ + s3sz );

hdr = ( hdr_t* )hdr;

s1 = align( hdr + sizeof( hdr_t ), ALSZ );

s2 = align( s1 + sizeof( s1 ), ALSZ );

s3 = align( s2 + sizeof( s2 ), ALSZ );

align( mem,boundary ) は、アラインされたアドレスを計算するための私のルーチンです。

ご感想ありがとうございます。

4

3 に答える 3

2

使用する代わりに

typedef struct
{
    s1_t*    s1;
    s2_t*    s2;
    s3_t*    s3;
} hdr_t;

使用する

typedef struct
{
    s1_t    s1;
    s2_t    s2;
    s3_t    s3;
} hdr_t;

main( int argc, char* argv[] )
{
    void*    mem = malloc( sizeof( hdr_t ));

    hdr_t*    hdr = ( hdr_t* )mem;
}

これにより、まさにあなたが探しているものが実現します。構造のセットにメモリを連続して割り当て、hdr_tチップメモリ​​のアライメントパディングを気にせずにのメンバーが正しい場所を指していることを保証します。

于 2012-06-25T01:39:23.803 に答える
1

ポインター演算に問題があります。ポインターに 1 を追加しても、ポインターは 1 バイトではなく、次の要素に進みます。先のとがったタイプのサイズによって。

あなたが持っている場合

struct foo {
    ...
};

struct bar {
    ...
};

struct baz {
    ...
};

struct header {
    ...
    struct foo *foo;
    struct bar *bar;
    struct baz *baz;
    ...
};

を使用して、単一のメモリブロックを使用して構造を割り当てることができます

struct header *ptr;

ptr = malloc (sizeof (struct header) + sizeof (struct foo) + sizeof (struct bar) + sizeof (struct baz));
if (!ptr) {
    /* out of memory */
    exit(1);
}

ptr->foo = (struct foo *)(ptr + 1);
ptr->bar = (struct bar *)(ptr->foo + 1);
ptr->baz = (struct baz *)(ptr->bar + 1);

別の方法として、バイト オフセットを使用する場合 (たとえば、構造体のサイズが動的である場合 - C99 では非常に合法的である場合)、次のように使用できます。

struct header *ptr;
char          *tmp;

tmp = malloc (sizeof (struct header) + sizeof (struct foo) + sizeof (struct bar) + sizeof (struct baz));
if (!tmp) {
    /* out of memory */
    exit(1);
}

ptr = (struct header *)tmp;
ptr->foo = (struct foo *)(tmp + sizeof (struct header));
ptr->bar = (struct bar *)(tmp + sizeof (struct header) + sizeof (struct foo));
ptr->baz = (struct baz *)(tmp + sizeof (struct header) + sizeof (struct foo) + sizeof (struct bar));

どちらの場合も、struct header構造内のポインターは、それ自体のポインターに対して同じ値を取得しますstruct header。後者の場合、任意の構造サイズを使用する場合は、ABI 要件ごとに新しいポインターを調整することも忘れないでください。

于 2012-06-25T02:13:57.980 に答える
0

Dancrumb のソリューションは、使用できる場合は間違いなく最適です。そうでない場合 (たとえば、必要な構造体の数が可変である場合、組み合わせが多すぎる場合、または特別なケースが法外にコストがかかる場合)、自分でアライメントを行うことができます。

型のアラインメント要件は常に型のサイズを分割するため、構造体に現れる可能性のあるすべての型の LCM を取得することから始めます。怠惰になりたい場合はsizeof(short)*sizeof(int)*sizeof(long)*...、すべてのタイプの製品を服用してください。または、それらのサイズよりも大きいと確信している 64 のような大きな 2 のべき乗を使用するだけです。

次に、malloc によって取得されたブロックでポインター演算を実行するときに、各構造体のサイズを上で選択したアライメント値の次の倍数に切り上げます。例えば

#define ROUND_UP(n, a) (((n)+((a)-1))/(a)*(a))
ps2 = (void *) ( (char *)ps1 + ROUND_UP(sizeof(*ps1), ALIGNMENT) );

whereps1は malloc されたブロックの最初の構造体へのps2ポインターであり、2 番目の構造体へのポインターです。

もちろん、十分なスペースがあることを確認するために、割り当てを行うときに同じ切り上げを行う必要があります。

編集:私の古い回答の1つで、これに対する改善が見つかりました。特に 100% 移植可能にしたい場合は、アラインメント要件の最大値を見つけようとするのは面倒なので、任意の型(構造体を含む) のアラインメント要件は型のサイズを分割する必要があるという事実を使用して、次のように仮定します。彼らは等しいです。あれは:

ps2 = (void *) ( (char *)ps1 + ROUND_UP(sizeof(*ps1), sizeof(*ps2)) );

これは、最初の構造体のサイズを 2 番目の構造体のサイズの次の倍数に切り上げます。これは少し無駄かもしれませんが、シンプルで常に機能するはずです。

于 2012-06-25T02:13:07.100 に答える