3

我一直认为在将 void* 转换为 struct* 后检查指针是避免无效转换的有效方法。就像是

MyStructOne* pStructOne = (MyStructOne*)someVoidPointer;
if(!pStructOne)
   return 0;

看来情况并非如此,因为我可以将相同的数据转换为两个不同的结构并获得相同的有效地址。然后程序很乐意用其中的任何随机数据填充我的结构字段。

什么是转换结构指针的安全方法?

我不能使用 dynamic_cast<> 因为它不是一个类。

谢谢您的帮助!

4

8 回答 8

2

如果您对结构布局有任何控制权,则可以将自己的类型枚举放在每个结构的前面以验证类型。这适用于 C 和 C++。

如果由于并非所有类型都提前知道而无法使用枚举,则可以使用 GUID。或者指向每个结构唯一的静态变量或成员的指针。

于 2012-09-23T00:15:53.607 回答
1

您可以dynamic_cast与结构或类一起使用,只要它具有虚拟方法即可。我建议您重新设计更广泛的系统,使其在void*任何地方都没有 s。这是非常糟糕的实践/设计。

于 2012-09-22T23:59:45.653 回答
1

一般来说,没有“安全的转换方式”,因为转换指针本质上是一个不安全的过程。铸造说你比类型系统更了解,所以你不能指望类型系统在你开始铸造指针之后有任何帮助。

在 C++ 中,您永远不应该使用 C 风格的强制转换(如(T) x),而应使用 C++ 强制转换。现在有一些简单的规则可以让您确定是否可以转换指针或引用:

  • 如果您const_cast在错误的方向上修改了对象,则必须确保该对象实际上是可变的。

  • 您只能static_cast在多态层次结构中使用指针或引用,或者从/到 void 指针。您必须确保对象的动态类型是强制转换目标的子类型,或者在 void 指针的情况下,该指针是正确类型对象的地址。

  • reinterpret_cast只能用于char *类型(可能是signedor unsigned)或从类型转换,或将指针转换为(u)intptr_t.

在任何情况下,都有责任确保所讨论的指针或引用引用您在强制转换中声明的类型的对象。没有其他人可以为您验证这一点的检查。

于 2012-09-23T00:07:36.233 回答
1

您正在使用的(C 风格)强制转换是编译时操作 - 也就是说,编译器生成指令来修改指向一个事物的指针,使其指向另一个事物。

对于继承关系,这只是指针的加法或减法。

就您的代码而言,编译器不会生成任何代码。演员表只是告诉编译器你知道你在做什么。

编译器不会生成任何代码来检查您的操作的有效性。如果someVoidPointer为空,则pStructOne在演员表之后也是如此。\

使用 adynamic_cast<>()并不能验证被强制转换的东西实际上是一个对象 - 它只是告诉您具有 RTTI 的对象是(或可以转换为)您期望的类型。如果它不是一个开始的对象,你很可能会崩溃。

于 2012-09-23T00:09:06.697 回答
0

没有一个。坦率地说,不可能。

struct只是编译器以sizeof()特定语义方式处理下一个字节的指令-仅此而已。

您可以将任何指针转换为任何指针 - 所改变的只是编译器如何解释内容。

使用dynamic_cast<>是唯一的方法,但它会调用 RTTI(运行类型类型信息)来考虑分配的潜在合法性。是的,它不再是reinterpret_cast<>

于 2012-09-23T00:00:42.853 回答
0

听起来您想确保作为 a 传递void*给您的函数的对象确实是您期望的类型。最好的方法是声明函数原型,MyStructOne*而不是void*让编译器进行类型检查。

如果你真的想要做一些更动态的事情(比如可以将不同类型的对象传递给你的函数),你需要启用RTTI。这将允许您询问传入的对象并询问它是什么类型。

于 2012-09-23T00:04:05.013 回答
0

什么是转换结构指针的安全方法?

首先,尽量避免一开始就要这样做。如果您不想包含它们的标题,请对结构使用前向声明。通常,如果一个函数可以采用多种类型的数据,您应该只需要从签名中隐藏数据类型。类似的例子是一个消息传递系统,你希望能够传递任意数据。发送者和接收者知道他们期望什么类型,但消息系统本身不需要知道。

假设您没有其他选择,请使用boost::any. 这本质上是类型安全的void*;尝试将其转换为错误的类型将引发异常。请注意,这需要 RTTI 才能工作(您通常应该有)。

请注意,boost::variant如果可以使用一组固定的、有限的可能类型,则可能会出现这种情况。

于 2012-09-23T00:16:13.830 回答
0

由于您必须使用void*,您的选择是:

  1. 创建一个包含虚拟析构函数(和/或其他虚拟方法)的单个基类,并在 libev 接口中独占使用它。包装 libev 接口以强制执行此操作,并且仅使用 C++ 代码中的包装器。然后,您的 C++ 代码中,您可以dynamic_cast创建基类。

  2. 接受您没有关于您void*真正指向的类型的任何运行时信息,并且只是构造您的代码,以便您始终静态地知道。也就是说,请确保首先转换为正确的类型。

  3. 使用void*来存储一个简单的标签/cookie/id 结构,并使用它来查找您的真实结构或其他任何东西 - 这实际上只是 #1 的一个更手动的版本,并且会导致额外的间接引导。


直接回答

什么是转换结构指针的安全方法?

是:

转换为正确的类型,或您知道与布局兼容的类型。

静态地知道正确的类型是什么,没有任何替代品。您大概将某些东西作为 a传入void*,因此当您取回它时,void*您应该能够知道它是什么类型。

于 2012-09-23T00:19:06.940 回答