C:一个malloced块,用于不同类型的多个结构

时间:2012-06-25 01:34:37

标签: c

尝试使用一个malloc()ed块来存储多个不同类型的结构。成员虚构用于演示目的。不能对它们的类​​型,顺序等做出任何假设,除非它们构成完全合法的C结构(完全为C编译器所知并为C编译器定义)。这一次,其他时间就是这样。我追求这个概念。创建自己的混合,但想法必须相同 - header struct有指向其他结构的指针:

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. */
}

上面(天真)代码是希望不用小mallocs对堆进行分段,而是为整个(在键盘时间已知)集合获取一个块。

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

换句话说,我需要(伪代码),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)是我计算对齐地址的例程。

感谢您的想法。

3 个答案:

答案 0 :(得分: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的成员指向正确的位置而不用担心芯片内存对齐填充; < / p>

答案 1 :(得分:0)

Dancrumb的解决方案绝对是最好的,如果你可以使用它。如果不是(例如,如果您需要的结构数量是可变的,或者组合太多或特殊套管的成本过高),您可以自己进行校准:

由于任何类型的对齐要求总是除以类型的大小,因此首先采用可能出现在结构中的所有类型的LCM。如果您想要懒惰,只需将产品sizeof(short)*sizeof(int)*sizeof(long)*...用于所有类型。或者只使用像64这样的两个大功率,你确定它们大于任何一个的大小。

然后,当对malloc获得的块进行指针运算时,将每个结构的大小向上舍入到您在上面选择的对齐值的下一个倍数。例如

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

其中ps1是指向malloc'd块中第一个结构的指针,ps2是指向第二个结构的指针。

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

编辑:我在一个旧答案中找到了改进。因为尝试找出最大可能的对齐要求是很痛苦的,特别是如果你想要100%便携,只需使用任何类型(包括结构)的对齐要求必须划分的事实类型的大小,并假设它们是相等的。那就是:

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

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

答案 2 :(得分:0)

您的指针算法有问题。向指针添加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要求对齐新指针。