【问题标题】:Safely casting struct pointers安全地转换结构指针
【发布时间】:2012-09-22 23:53:28
【问题描述】:

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

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

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

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

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

感谢您的帮助!

【问题讨论】:

  • 简单。使用 C++ 代替 void *
  • @shf301,但您仍然需要至少一个虚函数才能使用dynamic_cast,而结构通常没有。
  • dynamic_cast 绝对不适用于结构。我得到一个编译错误。我也无法避免使用 void*.我正在使用 libev,所以我唯一的选择是 void*
  • @JoshBrittain: dynamic_cast 当然可以使用结构。它使用的是没有虚拟成员函数的类型。
  • @JoshBrittain:你和 shf301 谈论的事情略有不同。在 C++ 中,structclass 是等价的,唯一的区别是默认访问说明符在每种情况下都是 public/private。另一方面,许多人使用struct 来指代POD 类型(其中其他属性没有虚函数)。将dynamic_cast 与没有虚函数的类一起使用是不合法的,无论该类是使用struct 还是class 关键字创建的。

标签: c++ casting struct


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 无法避免使用 void*。我正在使用 libev 库。
    【解决方案3】:

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

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

    • 如果你const_cast方向不好,修改了对象,一定要确定对象实际上是可变的。

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

    • reinterpret_cast 只能用于或来自char * 类型(可能是signedunsigned),或用于将指针与(u)intptr_t 相互转换。

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

    【讨论】:

      【解决方案4】:

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

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

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

      编译器不会生成任何代码来检查您的操作的有效性。如果someVoidPointer 为空,那么在转换后pStructOne 也将为空。 \

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

      【讨论】:

      • 我应该澄清一下 dynamic_cast 的编译错误是在 void* 上使用它时。看起来我可能需要重新设计,以便 void* 始终为 null 或只有一种类型。
      【解决方案5】:

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

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

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

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

      【讨论】:

        【解决方案6】:

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

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

        【讨论】:

          【解决方案7】:

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

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

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

          请注意,boost::variant 是可能的,如果有一组固定的、有限的可能类型可以使用。

          【讨论】:

            【解决方案8】:

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

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

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

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


            直接回答

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

            是:

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

            静态地知道什么是正确的类型是无可替代的。您大概将 something 作为 void* 传入,因此当您返回 void* 时,您应该能够知道它是什么类型。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2017-01-04
              • 2016-05-05
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多