gpt4 book ai didi

c - 没有 union 的结构类型别名/标记 union

转载 作者:太空狗 更新时间:2023-10-29 17:22:02 25 4
gpt4 key购买 nike

对于两个(或更多)struct:BaseSub,第一个(未命名)struct >,从 BaseSub 的转换/转换是否安全,反之亦然?

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

struct Sub{
struct{
int id;
// same '...'
};
// actual data
};

这些功能是否保证安全且技术上正确? (另外:零长度的 char data[] 成员是否必要且有用?)

struct Base * subToBase(struct Sub * s){
return (struct Base*)s;
}

struct Sub * baseToSub(struct Base * b){
if(b->id != SUB_ID){
return NULL;
}

return (struct Sub*)b;
}

编辑

我没有计划在 Sub 中嵌套比 Base 更远的部分,而是保留添加其他子类型的可能性(直接在 Base 下) code>) 之后无需更改 Base。我主要关心的是指向 struct 的指针是否可以在 Base 和任何 sub 之间安全地来回转换。如果能引用 (C11) 标准,我们将不胜感激。

编辑 v2

稍微更改了措辞以阻止 OOP/继承讨论。我想要的是一个没有 union 的标记 union ,以便以后可以扩展。我没有做额外嵌套的计划。需要其他子类型功能的子类型可以明确地这样做,而无需进行任何进一步的嵌套。


上下文

对于脚本解释器 1 我做了一个伪面向对象 tagged-union类型系统,没有 union。它有一个(抽象的) generic 基类型 Object 和几个(特定的) 子类型,例如 String, Number, List 等。每个类型-struct 都有以下未命名的 struct作为第一个成员:

#define OBJHEAD struct{    \
int id; \
int line; \
int column; \
}

id 标识对象的类型,linecolumn 应该(也)是不言自明的。各种对象的简化实现:

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

我知道最优雅和技术上最正确的方法是使用 enum 作为 idunion 作为各种子类型。但我希望类型系统是可扩展的(通过某种形式的类型注册表),以便以后可以添加类型而无需更改所有与 Object 相关的代码。

稍后/外部添加可能是:

typedef struct File{
OBJHEAD;

FILE * fp;
} File;

无需更改 Object

这些转换是否保证安全?

(至于小的宏滥用:OBJHEAD 当然会被广泛记录,因此其他实现者将知道哪些成员名称不应该使用。这个想法不是隐藏标题,而是每次都粘贴它。)

最佳答案

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

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

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

...可能是实现允许 Base 对象在 32 位边界上对齐,但要求 Sub 对象在 64 位边界上对齐,甚至更严格的。

这一切都不受 Base 是否具有灵活的数组成员的影响。

取消引用通过转换不同类型的指针值获得的一种类型的指针值是否安全是一个不同的问题。一方面,C 对实现如何选择布局结构的限制很少:成员必须按照它们声明的顺序进行布局,并且在第一个之前不能有任何填充,否则,实现可以自由支配。据我所知,在你的情况下,如果你的两个结构的匿名 struct 成员有多个成员,则不需要以相同的方式布置它们。 (如果他们只有一个成员,那么为什么要使用匿名结构?)假设 Base.data 的起始偏移量与 中匿名结构之后的第一个元素相同也是不安全的>子

在实践中,取消引用您的 subToBase() 的结果可能没问题,您当然可以实现测试来验证这一点。此外,如果您有一个 Base * 是通过从 Sub * 转换获得的,那么将其转换回来的结果,例如通过 baseToSub(),保证与原始 Sub * 相同(又是 C11 6.3.2.3/7)。在这种情况下,转换为 Base * 并返回对将指针取消引用为 Sub * 的安全性没有影响。

另一方面,虽然我很难在标准中找到它的引用,但我不得不说 baseToSub() 在一般情况下是非常危险的。如果一个 Base * 实际上并不指向一个 Sub 被转换为 Sub * (这本身是允许的),那么它是 < em>不 取消引用该指针以访问未与 Base 共享的成员是安全的。特别是,鉴于我上面的声明,如果引用的对象实际上是一个 Base,那么声明 Base.data 绝不会阻止 ((Sub *) really_a_Base_ptr)->value 产生未定义的行为。

为了避免所有未定义和实现定义的行为,您需要一种避免转换并确保一致布局的方法。 @LoPiTaL 建议在您的 Sub 结构中嵌入一个类型化的 Base 结构,在这方面是一个很好的方法。

关于c - 没有 union 的结构类型别名/标记 union ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32638712/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com