Kni*_*nug 6 c language-lawyer c11
对于两个(或更多)struct
S:Base
和Sub
具有共同第一(未命名)struct
,是安全的转换/从投Base
至Sub
反之亦然?
struct Base{
struct{
int id;
// ...
};
char data[]; // necessary?
}
struct Sub{
struct{
int id;
// same '...'
};
// actual data
};
Run Code Online (Sandbox Code Playgroud)
这些功能是否保证安全且技术上正确?(另外:零长度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;
}
Run Code Online (Sandbox Code Playgroud)
编辑
我没有计划Base
在内部进一步嵌套Sub
,而是可以在以后添加其他子类型(直接在下面Base
),而无需更改Base
.我主要担心的是指针是否struct
可以安全地在指针Base
和任何子指针之间来回转换.对(C11)标准的参考将是最受欢迎的.
编辑v2
稍微改变了措辞以阻止OOP /继承讨论.我想要的是一个标记联合,没有union
它,所以它可以在以后扩展.我没有计划进行额外的嵌套.需要其他子类型功能的子类型可以明确地执行此操作,而无需进行任何进一步的嵌套.
对于脚本解释器1,我制作了一个伪面向对象的 标记联合类型系统,没有union
.它有一个(摘要) 通用基本类型Object
与几个(特定)的子类型,如String
,Number
,List
等,每个类型- struct
具有以下无名struct
作为第一构件:
#define OBJHEAD struct{ \
int id; \
int line; \
int column; \
}
Run Code Online (Sandbox Code Playgroud)
所述id
识别对象的类型,line
并且column
应该(也)是不言自明.各种对象的简化实现:
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;
}
Run Code Online (Sandbox Code Playgroud)
我知道,最优雅和技术上是正确的方式做,这将是使用enum
的id
和union
针对各种亚型.但我希望类型系统是可扩展的(通过某种形式的类型注册表),以便以后可以添加类型而不需要更改所有Object
相关的代码.
后期/外部添加可以是:
typedef struct File{
OBJHEAD;
FILE * fp;
} File;
Run Code Online (Sandbox Code Playgroud)
无需改变Object
.
这些转换是否保证安全?
(至于小的宏观滥用: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;
Run Code Online (Sandbox Code Playgroud)
所以现在,您可以将 Sub 结构强制转换为 Base 结构,或者只返回第一个成员,该成员已经是 Base 类型,因此不再需要强制转换。
需要注意的是:不要滥用宏。宏对于很多事情来说都很好,但滥用它们可能会导致代码难以阅读和维护。在这种情况下,宏很容易被替换为基本成员。
最后一句话,您的宏很容易出错,因为成员名称现在被隐藏了。最后,您可能会添加同名的新成员,并在不知道原因的情况下收到奇怪的错误。
当您进一步将层次结构扩展为子子类时,您最终将不得不编写所有基类宏,而如果您使用“继承”方法,则只需编写直接基类。
这些解决方案都没有真正解决您的问题:继承。您拥有的唯一真正的解决方案(首选)是更改为真正的面向对象语言。由于与 C 相似,最好的匹配是 C++,但也可以使用任何其他语言。