C 结构类型别名/无并集标记并集

C 结构类型别名/无并集标记并集,c,language-lawyer,c11,C,Language Lawyer,C11,对于两个(或多个)structs:Base和Sub具有共同的第一个(未命名)struct,从Base转换/转换为Sub是否安全,反之亦然 struct Base{ struct{ int id; // ... }; char data[]; // necessary? } struct Sub{ struct{ int id; // same '...' }; // actual d

对于两个(或多个)
struct
s:
Base
Sub
具有共同的第一个(未命名)
struct
,从
Base
转换/转换为
Sub
是否安全,反之亦然

struct Base{
    struct{
        int id;
        // ...
    };
    char data[]; // necessary?
}

struct Sub{
    struct{
        int id;
        // same '...'
    };
    // actual data
};
这些功能是否保证安全且技术正确?(另外:零长度
字符数据[]
成员是否必要且有用?)

编辑

我不打算在
Sub
中嵌套
Base
以外的任何子类型,而是允许以后添加其他子类型(直接在
Base
下),而无需更改
Base
。我主要关心的是指向
struct
s的指针是否可以在
Base
和任何sub之间安全地来回转换。最好参考(C11)标准

编辑v2

稍微更改了措辞,以阻止OOP/继承讨论。我想要的是一个带标签的联合体,没有
联合体
,因此可以在以后进行扩展。我没有做额外筑巢的计划。需要其他子类型功能的子类型可以显式地这样做,而无需进行任何进一步的嵌套


上下文 对于脚本解释器,我制作了一个伪面向对象类型系统,没有
联合
。它有一个(抽象的)泛型基类型
对象
,有几个(特定的)子类型,例如
字符串
数字
列表
等。每个类型-
结构
都有以下未命名的
结构
作为第一个成员:

#define OBJHEAD struct{    \
    int id;                \
    int line;              \
    int column;            \
}
id
标识对象的类型,
也应该是自解释的。各种对象的简化实现:

typedef struct Object{
    OBJHEAD;

    char data[]; // necessary?
} Object;

typedef struct Number{
    OBJHEAD;

    int value; // only int for simplicity
} Number;

typedef struct String{
    OBJHEAD;

    size_t length;
    char * string;
} String;

typedef struct List{
    OBJHEAD;

    size_t size;
    Object * elements; // may be any kind and mix of objects
} List;

Object * Number_toObject(Number * num){
    return (Object*)num;
}

Number * Number_fromObject(Object * obj){
    if(obj->type != TYPE_NUMBER){
        return NULL;
    }

    return (Number*)obj;
}
我知道,最优雅、技术上最正确的方法是对
id
使用
enum
,对各种子类型使用
union
。但是我希望类型系统是可扩展的(通过某种形式的类型注册表),以便以后可以添加类型,而无需更改所有与
对象相关的代码

以后的/外部添加可以是:

typedef struct File{
    OBJHEAD;

    FILE * fp;
} File;
无需更改
对象

这些转换是否保证安全


(至于小的宏滥用:
OBJHEAD
当然会被广泛地记录下来,这样更多的实现者就会知道哪些成员名称不可以使用。其目的不是隐藏头,而是避免每次粘贴头。)

这是正确的,因为它保证在结构的第一个成员上具有相同的对齐方式,因此,您可以从一个结构转换到另一个结构

然而,实现行为的常用方法是“继承”基类:

//Base struct definition
typedef struct Base_{
    int id;
    // ...
    //char data[]; //This is not needed.
}Base;

//Subclass definition
typedef struct Sub_{
    Base base;  //Note: this is NOT a pointer
    // actual data
}Sub;
现在,您可以将子结构强制转换为基本结构,或者只返回第一个成员,它已经是Base类型,因此不再需要强制转换

警告一句:不要滥用宏。宏在很多方面都很好,但滥用宏可能会导致代码难以读取和维护。 在这种情况下,宏很容易被基成员替换

最后一句话,您的宏很容易出错,因为成员名称现在已隐藏。最后,您可能会添加同名的新成员,并在不知道原因的情况下出现奇怪的错误

当您进一步将层次结构扩展到子类时,您将不得不编写所有的基类宏,而如果您使用“继承”宏,则必须只编写直接基类


这两种解决方案实际上都不能解决您的问题:继承。唯一真正的解决方案(首选)是更改为trully OO语言。由于与C相似,最好的匹配是C++,但可以做任何其他语言。

< P>不,它不安全,至少在所有情况下都不安全。如果编译器看到两个指针
p
q
具有不同的基类型,它可能总是假定它们没有别名,或者说,它可能总是假定
*p
*q
是不同的对象

你的演员在这个假设上打了个洞。如果你有一个函数

double foo(struct A* p, struct B* q) {
   double b = q->field0;
   *p = (struct A*){ 0 };
   return b + q->field0;       // compiler may return 2*b
}
允许优化器避免额外的内存读取


如果您知道没有函数会通过不同类型的指针看到相同的对象,那么您就安全了。但这样的断言并不容易,所以最好避免这种黑客行为。

允许将指向一种对象类型的指针转换为指向另一种对象类型的指针(例如,通过强制转换),但如果生成的指针未正确对齐,则行为未定义(C11 6.3.2.3/7)。根据
Base
Sub
的成员以及依赖于实现的行为,转换为
Sub*
Base*
不一定正确对齐。例如,给定

struct Base{
    struct{
        int id;
    };
    char data[]; // necessary?
}

struct Sub{
    struct{
        int id;
    };
    long long int value;
};
。。。可能是该实现允许
Base
对象在32位边界上对齐,但要求
Sub
对象在64位边界上对齐,甚至在更严格的边界上对齐

所有这些都不受
Base
是否具有灵活的数组成员的影响

解除引用通过强制转换不同类型的指针值而获得的一种类型的指针值是否安全是另一个问题。首先,C对实现如何选择布局结构没有太多限制:成员必须按照声明的顺序进行布局,并且在第一个成员之前不能有任何填充,否则,实现可以自由支配。据我所知,在您的情况下,如果两个结构的匿名
struct
成员有多个成员,则不要求它们的布局方式必须相同。(如果他们只有一个我
struct Base{
    struct{
        int id;
    };
    char data[]; // necessary?
}

struct Sub{
    struct{
        int id;
    };
    long long int value;
};