void *作为C中的泛型,是否安全?

时间:2015-08-06 23:24:30

标签: c generics

为一个有点“扭曲”的问题做好准备...

我在过去实现了很多数据结构(树,列表,哈希表,图表),使用宏我可以实现某种通用。然而,如果有可能使用void指针实现通用数据结构,我会徘徊但不知何故,我希望能够使用类型转换......

我不知道我想说的是否清楚...但基本上我认为将“void *”作为通用并不总是安全的,同时我不这样做认为使用宏作为制作通用数据结构的方式总是一个好主意(因为基本上预处理器对宏所做的是代码替换),因为如果你环顾网络就可以找到这样的例子。

一个好主意可能是,在我看来,但可能我不对,就是使用宏为数据结构中存储的数据制作标准接口,在接口函数中我会把代码设置为正确的类型检查,给出无效*。受软件工程师techinique的启发,这可能是一个很好的方法。

对于转换语言(C ++ / Java)来说,可能过于软化的东西肯定是更好的,但事实并非总是如此。

总结一下...... C中的“泛型”问题通常是如何处理的?我依靠你的经验来回答!

2 个答案:

答案 0 :(得分:6)

简而言之,在C语言中没有方便的方法来获得类型安全的通用数据结构和函数。

非通用:

struct node {
  int value;
  struct node *next;
};

通用,但不安全 - void*没有类型信息:

struct node {
  void *value;
  struct node *next;
};

安全,但丑陋:

#define DECLARE_NODE_TYPE(type) \
  struct node_##type { \
    type value; \
    struct node_##type *next; \
  };

DECLARE_NODE_TYPE(int)
node_int *x = ...

同样的想法,但稍微不那么难看:

// declare_node_type.h

struct node_##NODE_TYPE {
  NODE_TYPE value;
  struct node_##NODE_TYPE *next;
};

#undef NODE_TYPE

// elsewhere

#define NODE_TYPE int
#include "declare_node_type.h"

node_int *x = ...

通用且安全,但C ++,而不是C:

template<typename T>
struct node {
  T value;
  node<T> *next;
};

node<int> *x = ...

答案 1 :(得分:1)

您可以使用void*执行更安全的内容;回到Jon Purdy的链接示例:

typedef struct {
    union {
        void* data;  // generic data
        int idata;   // int is not stored dynamically
    };
    int type;    // additional type information
    Node* next;  // link
} Node;

#define NODE_TYPE_INT 0
Node* createNodeInt(Node* self, Node* next, int value) {
    self->idata = value;
    self->type = NODE_TYPE_INT;
    self->next = next;
    return self;
}

// in this case relying on user defined types...
Node* createNodeGeneric(Node* self, Node* next, void* data, int type) {
    assert(type != NODE_TYPE_INT && ..);
    self->data = data;
    self->type = type;
    self->next = next;
    return self;
}

另一种方法是使用公共第一个成员作为基本类型:

typedef struct {
    int type;
} Node;

#define TYPE_BINARY 0
typedef struct {
    Node base;
    Node* left;
    Node* right;
    int op;
} BinaryOp;

#define TYPE_LEAF_INT 1
typedef struct {
    Node base;
    int a;
} LeafInt;

#define TYPE_LEAF_FLOAT 2
typedef struct {
    Node base;
    float b;
} LeafFloat;

void op(BinaryOp* node) {
    switch(node->left.type) {
    case TYPE_BINARY:
        op((BinaryOp*)node->left);
        break;
    case TYPE_LEAF_INT:
        evalInt((LeafInt*)node->left);
        break;
    ...
    }
}

Node* foo() {
    LeafInt* left;
    LeafFloat* right;
    BinaryOp* op;
    // allocate
    ...
    // init
    createLeafInt(left, 42);
    createLeafFloat(right, 13.37);
    createBinaryOp(op, &left->base, &right->base);
    // and return
    return &op->base;
}
}