如何检查void *指针是否可以安全地转换为其他内容?

时间:2012-02-01 16:26:05

标签: c pointers casting void void-pointers

假设我有这个函数,它是某些gui工具包的一部分:

typedef struct _My_Struct My_Struct;
/* struct ... */

void paint_handler( void* data )
{
   if ( IS_MY_STRUCT(data) ) /* <-- can I do something like this? */
   {
      My_Struct* str = (My_Struct*) data;
   }
}

/* in main() */
My_Struct s;
signal_connect( SIGNAL_PAINT, &paint_handler, (void*) &s ); /* sent s as a void* */

由于paint_handler也将由GUI工具包的主循环​​与其他参数一起调用,我不能总是确定我收到的参数将始终是指向s的指针。

我可以在IS_MY_STRUCT函数中执行paint_handler之类的操作来检查我收到的参数是否可以安全地转回My_Struct*

6 个答案:

答案 0 :(得分:18)

您的void指针会丢失其所有类型信息,因此仅凭此,您无法检查是否可以安全地投射它。程序员可以知道void*是否可以安全地转换为类型。

答案 1 :(得分:3)

不幸的是,没有函数检查指针在该上下文中出现之前是什么(void)。

我能想到的一个解决方案是将int _struct_id作为所有结构的第一个成员。然后可以安全地检查此id成员而不管类型如何,但如果传递未实现此成员的指针(或int,char,...指针),则会失败。

答案 2 :(得分:3)

你能做的最好的事情就是查看哪些数据指向它是否有明确的迹象表明你想要什么,尽管a)它不会接近保证而b)可能是危险的,因为你不知道数据实际指向的有多大。我认为它不仅仅是铸造它并使用它更危险,但是(正如已经建议的那样)重新设计会更好。

答案 3 :(得分:0)

确实没有。 void指针是无类型的,只有当你真正知道它们指向的内容时才应该进行投射。

也许您应该重新考虑您的设计;重写您的代码,以便不需要检查。这与google在其style guide中禁止RTTI的原因相同。

答案 4 :(得分:0)

如果要创建正在使用的类型,则可以在类型中包含某种标识信息,这些信息可以帮助您排除某些void指针,因为它们不是您正在寻找的类型。虽然您可能会有一些随机区域的内存包含与您要查找的内容相同的数据或签名,但至少您会知道某些内容不是您要查找的类型。

这种方法要求对结构进行初始化,使得用于确定存储区域是否无效的签名成员被初始化为签名值。

一个例子:

typedef struct {
    ULONG  ulSignature1;
    //  .. data elements that you want to have
    ULONG  ulSignature2;
} MySignedStruct;
#define MYSIGNEDSTRUCT_01  0x1F2E3D4C
#define MYSIGNEDSTRUCT_02  0xF1E2D3C4

#define IS_MY_STRUCT(sAdr)  ( (((MySignedStruct *)sAdr)->ulSignature1 == MYSIGNEDSTRUCT_01  ) && (((MySignedStruct *)sAdr)->ulSignature1 == MYSIGNEDSTRUCT_02))

这是一种粗略的方法,但它可以提供帮助。如果参数有副作用,自然地使用像IS_MY_STRUCT()那样参数被使用两次的宏可能会有问题所以你必须小心IS_MY_STRUCT(xStruct++)之类的地方,其中xStruct是一个指针到MySignedStruct

答案 5 :(得分:0)

我知道这个问题是3岁但是我走了, 如何使用简单的全局枚举来区分函数的调用位置。然后你可以在什么类型之间切换以将void指针转换为。