在C语言中,不同大小的结构之间的转换是否允许/安全?

在C语言中,不同大小的结构之间的转换是否允许/安全?,c,pointers,struct,casting,type-conversion,C,Pointers,Struct,Casting,Type Conversion,我有一些类似这样的结构: 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-&g

我有一些类似这样的结构:

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
s和指针都是使用malloc分配的(当然不能为NULL)

以下代码安全吗

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

void deallocMyStructX(void* myStructX) {
    genericForm* x = (genericForm*)myStructX;
    x->deallocMe(myStructX);
}
我为什么要这样做

我将有一个指向某些
struct
的指针数组,如上图所示,如果这样做可行,我就不必为每个
struct
/指针保存deallocator。这将真正简化我的代码

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

如果您只想访问结构的第一个成员,那么就可以了。第一个成员的偏移量保证为0,如果第一个成员在所有结构(即函数指针)中都兼容,则应该是正确的。如果要取消对结构的引用并访问其他成员,则可能会由于填充而遇到问题。添加占位符成员可能会解决这个问题,但可以说效率有点低

如果您不需要担心太多的结构,那么在这里,a可能是一个更简单的解决方案:

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),这应该有效:

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


如果您只想访问结构的第一个成员,那么就可以了。第一个成员的偏移量保证为0,如果第一个成员在所有结构(即函数指针)中都兼容,则应该是正确的。如果要取消对结构的引用并访问其他成员,则可能会由于填充而遇到问题。添加占位符成员可能会解决这个问题,但可以说效率有点低

如果您不需要担心太多的结构,那么在这里,a可能是一个更简单的解决方案:

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),这应该有效:

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

在C语言中,不同大小的结构之间的转换是否允许/安全

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

解决方案是将这两个结构放在一个
联合体中

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;
  }; 
};
在C语言中,不同大小的结构之间的转换是否允许/安全

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

解决方案是将这两个结构放在一个
联合体中

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;
  }; 
};

多么糟糕的问题标题!为什么不把“跟随”换成跟随的东西?!?使用类似的东西是使用C结构处理某种继承的一种非常常见的方法。但是,您的代码有一个问题,要想真正起作用,第二个结构需要看起来与第一个结构完全相同,即公共成员必须以完全相同的顺序声明。非常糟糕,我认为他应该被枪毙。另一种选择是让一个
struct genericForm
作为其他
struct
s中的第一个成员。可能是匿名成员。如果这样做有效,我就不必为每个结构/指针保存deallocator。但是您必须保存deallocator—您只需将其作为结构的一部分保存即可。所以你什么也得不到。多么糟糕的问题标题!为什么不把“跟随”换成跟随的东西?!?使用类似的东西是使用C结构处理某种继承的一种非常常见的方法。但是,您的代码有一个问题,要想真正起作用,第二个结构需要看起来与第一个结构完全相同,即公共成员必须以完全相同的顺序声明。非常糟糕,我认为他应该被枪毙。另一种选择是让一个
struct genericForm
作为其他
struct
s中的第一个成员。可能是匿名成员。如果这样做有效,我就不必为每个结构/指针保存deallocator。但是您必须保存deallocator—您只需将其作为结构的一部分保存即可。所以你什么也得不到。嗯,我可能会得到很多不同的结构,所以你的代码会很快变得非常复杂。但我需要的是这样一句话:“第一个成员的偏移量保证为0,如果第一个成员在所有结构(即函数指针)中都兼容,那么你应该没问题。”!Ty@K.Biermann:引用了标准中确认第一个成员将始终位于偏移量0的部分,并添加了一个允许您强制转换结构并直接调用其第一个成员的行程序。嗯,我可能会得到许多不同的结构,因此您的代码将非常复杂。但我需要的是这样一句话:“第一个成员的偏移量保证为0,如果第一个成员在所有结构(即函数指针)中都兼容,那么你应该没问题。”!Ty@K.Biermann:引用了标准中确认第一个成员将始终位于偏移量0的部分,并添加了一个允许您强制转换结构并直接调用其第一个成员的行程序,那么这个sockaddr sockaddr_内部如何工作?你没有任何反对“指向结构对象的指针,经过适当转换,指向它的初始成员[…],反之亦然。在as结构对象中可能有未命名的填充,但不是在它的开头。”的论点,这就是我想知道的;你是答案