0

尝试使用一个 malloc()ed 块在其中存储多个不同类型的结构。成员出于演示目的是虚构的。不能对它们的类​​型、顺序等做出任何假设,除非它们构成了完全合法的 C 结构(完全为 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 将堆碎片化,而是为整个(在键盘时间已知)集合获得一个块。

问题:对于尽可能便携的解决方案,我是否必须在我的方案中手动对齐结构?

换句话说,我是否需要(伪代码),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 加到指针上并不是将其前移一个字节,而是将其前移到下一个元素;通过尖类型的大小。

如果你有

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'd 块中第一个结构的指针,ps2是指向第二个结构的指针。

当然,您需要在进行分配时进行相同的舍入以确保有足够的空间。

编辑:我在我的一个旧答案中发现了对此的改进。由于试图找出最大可能的对齐要求是很痛苦的,特别是如果你想 100% 可移植,只需使用任何类型(包括结构)的对齐要求必须除以类型的大小的事实,并假设他们是平等的。那是:

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

它将第一个结构的大小四舍五入到第二个结构大小的下一个倍数。这可能有点浪费,但它很简单并且应该始终有效。

于 2012-06-25T02:13:07.100 回答