如何检查C中的结构类型

Fab*_*otk 4 c

我在 C 中创建了一个简单的列表结构,它使用 void* 指针保存任何类型的数据,例如:

struct node
{
    void *the_data;
    node *next;
};
Run Code Online (Sandbox Code Playgroud)

它工作得很好,但我有一些列表,例如,一个列表中只包含 struct_a,另一个列表中只包含 struct_b。我需要在单个 if() 问题中检查列表的第一个元素是 struct_a 类型还是 struct_b 类型,以便我知道该列表保存的数据。这该怎么做?谢谢

编辑:

我想出了一个简单的解决方案,现在已经足够解决这个问题了,不知道它是否是最好的:

struct node
{
    void *data;
    node *next;
}
Run Code Online (Sandbox Code Playgroud)

我添加了一个“描述符”节点,其中包含第一个节点的地址和有关类型的信息:

struct list_desc
{
    node *list;
    short int type;
}
Run Code Online (Sandbox Code Playgroud)

我会将它与一些宏一起使用,例如

#define __data_type1 10
#define __data_type2 20
Run Code Online (Sandbox Code Playgroud)

所以稍后我可以比较 if( struct.type == __data_tipe1 ) 等...

too*_*ite 5

C 不提供运行时类型信息。您必须自己添加此信息。有两种基本方法(假设为 C99 或 C11):

您可以将结构(可能)与匿名联合一起使用:

struct Node {
    enum {
        OBJ_TYPE_A,
        OBJ_TYPE_B
    } type;
    union {
        struct NodeA a;
        struct NodeB b;
    };    // anonymous, if a name is added, you have an additional field name
};

...

struct Node *node = ...;

if ( node->type == OBJ_TYPE_A ) {

    node->a.a_field = whatever;

...
Run Code Online (Sandbox Code Playgroud)

缺点是你必须在一个地方定义联合并且不能在另一个模块中扩展它。


更好的方法是使用匿名结构构建真正的类层次结构:

struct Node {
    enum {
        OBJ_TYPE_A,
        OBJ_TYPE_B
    } type;
    struct Node *next;
};

struct NodeA {
    struct Node;    // base class, first field!
    int a_field;
};

struct NodeB {
    struct Node;    // base class, first field!
    float b_field;
};

void take_node(struct Node *node)
    // takes a generic pointer to the base-class
{

    if ( node->type == OBJ_TYPE_A ) {
        struct NodeA *a_node = (NodeA)node;
        a_node->a_field = whatever;
    } else if ...

    ...

}

// until here that is normal C99

int main(void)
{
    struct NodeA anode = { .type = OBJ_TYPE_A, .a_field = 42 };
    struct NodeB bnode = { .type = OBJ_TYPE_B, .b_field = 4.2 };
    take_node((Node *)&anode);    // standard: inhibits type checking
    take_node(&anode);    // plan9-extension: no cast required
    take_node(&bnode);    // plan9-extension: no cast required
}
Run Code Online (Sandbox Code Playgroud)

优点是在创建新的子结构时不需要扩展基本结构(它不依赖于子结构)。匿名结构允许创建直接子代的子代和访问字段,正如在 OOP 中所期望的那样(没有字段说明符链:child1_1_1_ptr->child1_1.child1.base.base_field,而只是child1_1_1_ptr->base_field)。

后两个调用take_node使用 gcc C-extension -fplan9-extensions。这避免在仍然启用类型检查(基类或子类)的同时转换为基类。这已经是向 OOP 迈出的一大步。

这可以扩展到全尺寸的 OOP 方法,包括虚拟方法。然而,有一点像 C++ 那样使用真正的 OOPL 会变得更容易。


wal*_*ruz 1

您对此有何看法:

typedef enum tag_ElementType
{
    ElementA,
    ElementB
} en_ElementType;

typedef struct tag_Container
{
    en_ElementType ElementType;
    void *pData;
} st_Container;
Run Code Online (Sandbox Code Playgroud)

在这种情况下,您的结构将如下所示:

typedef struct tag_node
{
    st_Container the_data;
    struct tag_node *next;
} st_node;
Run Code Online (Sandbox Code Playgroud)