整个代码是用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
是否存在根本错误,反之亦然?这种方式有什么可能的陷阱?关于可测试性和可维护性的任何想法?
编辑:代码现在编译,链接并运行正常。我想知道为什么不应该这样做,以及是否有任何真正的原因必须更改代码。
答案 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_t
和uintptr_t
,它们是整数类型,并且保证将void *
转换为它们并返回。
答案 1 :(得分:0)
您正在做的是实施定义。在转换为无效点然后转换回来之后,无法保证枚举具有相同的值。如果你真的需要确定,你应该使用intptr_t或uintptr_t而不是枚举。它们被保证在被转换为指向void的指针后再返回时具有相同的值。
答案 2 :(得分:0)
Alok绝对正确,整数和指针之间的转换是实现定义的,因此完全不可移植。这似乎是完全允许的,例如,如果void *
值的所有转换为整数值总是产生0(无论如何,在不提供intptr_t
或uintptr_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 *将是未定义的。