将类型转换为无效*是错误/危险的吗?

时间:2010-02-25 02:30:27

标签: c type-conversion

整个代码是用ANSI C编写的,它应该保持不变。 我有一个像这样定义的回调:

typedef enum {
    Event_One,
    Event_Two,
    Event_State
} EventEnum;

typedef void (*callback)(EventEnum event, void* data);

回调收件人根据data值解释event。这是组件之间的契约。有时它是指向结构的指针,有时它可能是一个字符串,其他情况可能是其他数据。我正在定义一个额外的event并设置一个新的“合同”,data是一个枚举。像这样:

typedef enum {
    State_Initial = 0,
    State_Running,
    State_Final
} StateEnum;

然后代码中的某处我有一个回调函数,就是这样做

void ProcessEvent (EventEnum event, void* data)
{
    if (event == Event_State)
    {
         StateEnum state = (StateEnum)data; /* <<<<<<<<<<< */
         switch (state) {
         case State_Initial:
             <...>
             break;
         case State_Running:
             <...>
             break;
         case State_Final:
             <...>
             break;
         }
    }
}

上面的回调调用如下:

{
    callback infoCallback = ProcessEvent; /* This is only for example,
                                             done during initialization */
    <...>
    StateEnum someState = State_Running;
    <...>
    infoCallback(Event_State, (void*)someState); /* <<<<<<<<<<<<<<<<<<< */
}

void*类型转换为StateEnum是否存在根本错误,反之亦然?这种方式有什么可能的陷阱?关于可测试性和可维护性的任何想法?

编辑:代码现在编译,链接并运行正常。我想知道为什么不应该这样做,以及是否有任何真正的原因必须更改代码。

4 个答案:

答案 0 :(得分:5)

只有指向对象的指针(即非函数)才能转换为void *并返回。您无法将非指针转换为void *并返回。因此,请将您的电话更改为:

infoCallback(Event_State, &someState);

你的职能是:

StateEnum *state = data;
switch (*state)
...

从标准(6.3.2.3):

  

整数可以转换为任何指针类型。除非先前指定,否则结果是实现定义的,可能未正确对齐,可能不指向引用类型的实体,并且可能是陷阱表示。

     

任何指针类型都可以转换为整数类型。除了之前指定的以外,结果是实现定义的。如果结果无法以整数类型表示,则行为未定义。结果不必在任何整数类型的值范围内。

所以,你正在做的是实现定义。如果您的实现将其定义为可以将int转换为指针并返回,则代码将起作用。 一般,它不可移植。有关更多详细信息,请参阅comp.lang.c上的this thread

C99还定义了类型intptr_tuintptr_t,它们是整数类型,并且保证将void *转换为它们并返回。

答案 1 :(得分:0)

您正在做的是实施定义。在转换为无效点然后转换回来之后,无法保证枚举具有相同的值。如果你真的需要确定,你应该使用intptr_t或uintptr_t而不是枚举。它们被保证在被转换为指向void的指针后再返回时具有相同的值。

答案 2 :(得分:0)

Alok绝对正确,整数和指针之间的转换是实现定义的,因此完全不可移植。这似乎是完全允许的,例如,如果void *值的所有转换为整数值总是产生0(无论如何,在不提供intptr_tuintptr_t的实现上)。

一个常见的习惯用法是扩展契约,使得回调将接收一个指向动态分配的枚举的指针,它必须释放。在来电者中:

StateEnum *someState = malloc(sizeof *someState);
*someState = State_Running;

infoCallback(Event_State, someState);

...并在回调中:

void ProcessEvent (EventEnum event, void* data)
{
    if (event == Event_State)
    {
         StateEnum state = *(StateEnum *)data;
         free(data);
         switch (state) {

答案 3 :(得分:0)

虽然编译器允许这样做,但在某些情况下可能没问题,你需要非常小心对象的生命周期。

我可以看到的主要问题是除非processEvent函数与infoCallback在同一个调用链中发生,否则原始someState变量可能超出范围。所以稍后当你在infoCallback中引用时,void *将是未定义的。