是否在C中允许/安全地使用不同尺寸的结构?

时间:2015-12-18 16:56:51

标签: c pointers struct casting type-conversion

我有一些struct这样:

struct myStruct0 {
    void(*deallocMe)(void*); // points always to "myStruct0_dealloc"
    uint8_t* someStuff;
    void* someOtherStuff;
}
void myStruct0_dealloc(void* structPtr) {
    myStruct0* s = (myStruct0*)structPtr;
    free(s->someStuff);
    free(s->someOtherStuff);
    free(s);
}

struct myStruct1 {
    void(*deallocMe)(void*); // points always to "myStruct1_dealloc"
    uint8_t* someStuff;
    uint64_t largeArray[4096];
    void* someOtherStuff;
    char* someRealOtherStuff;
}
void myStruct1_dealloc(void* structPtr) {
    myStruct1* s = (myStruct1*)structPtr;
    free(s->someStuff);
    free(s->someOtherStuff);
    free(s->someRealOtherStuff);
    free(s);
}

struct和指针都使用malloc分配(当然不能为NULL)。

以下代码是否安全?

struct genericForm {
    void(*deallocMe)(void*);
}

void deallocMyStructX(void* myStructX) {
    genericForm* x = (genericForm*)myStructX;
    x->deallocMe(myStructX);
}

为什么我要这样做?

我将有一些指向某些struct的指针,如上所述,如果这样可行,我不必为每个struct /指针保存解除分配器。这样可以简化我的代码。

编辑: 结构可以完全不同。他们唯一共享的是它们包含一个void(*)(void*)形式的函数指针作为第一个元素。

2 个答案:

答案 0 :(得分:5)

如果您只想访问结构的第一个成员,那么您应该没问题。第一个成员的偏移量保证为0,如果第一个成员与所有结构(即函数指针)兼容,那么你应该没问题。如果您要取消引用结构并访问其他成员,则可能会因填充而遇到问题。添加占位符成员可能会解决这个问题,但可能有点效率低下。

如果你没有太多的结构要担心,这可能是一个更容易的解决方案:

struct _data {
    int identifier;
    union {
        struct type_1 {} _t1;
        struct type_2 {} _t2;
        struct _generic {
            void (*dealloc_generic)(void *);//first member is dealloc function pointer
        } _gen;
    };
};
void dealloc_struct(struct _data * s)
{
    switch (s->identifier)
    {
        case 1:
            s->t1->delloc_t1_ptr(s->t1);
            return;
        //and so on
        default:
            //for generic-compatible structs
            s->_gen->delloc_generic(s->_gen);
    }
}

如果你的结构的第一个成员总是一个有效的函数指针,那么你甚至可以这样写:

void dealloc_struct(void *s)
{
    //cast s to function pointer, call it and pass the pointer as argument
    ((void (*)(void *))s)(s);
}

根据标准(C1x§6.7.2.1.13),这应该有效:

  

指向适当转换的结构对象的指针指向其初始成员[...],反之亦然。在结构对象中可能有未命名的填充,但不在其开头。

答案 1 :(得分:1)

  

在C中允许/安全使用不同大小的结构?

一般来说:没有。一个结构的寻址空间可能与另一个结构完全不兼容。

解决方案是将两个结构放在union

struct myStruct0 {
  void(*deallocMe)(void*);  //Common elements must be in the beginning with same type/order
  ...
}

struct myStruct1 {
  void(*deallocMe)(void*);
  ...
}

union genericForm {
  struct myStruct0 s0;
  struct myStruct1 s1;
}

使用迂腐方法

struct myStruct0 {
  ...
};

struct myStruct1 {
  ...
};

struct genericForm {
  void(*deallocMe)(void*);   //Common elements here
  union {                    // Anonymous union
    struct myStruct0 s0;
    struct myStruct1 s1;
  }; 
};