【问题标题】:Why can't static_cast a double void pointer?为什么不能 static_cast 双空指针?
【发布时间】:2014-12-04 07:45:02
【问题描述】:

考虑以下代码:

void **v_dptr(nullptr);
int  **i_dptr = static_cast<int**>(v_dptr);

上面的例子产生如下编译错误:

static_cast from 'void **' to 'int **' is not allowed

LIVE DEMO

我知道将void 指针转换为任何其他指针类型的正确方法是使用static_cast。但是,您不能 static_cast 一个双精度指针 void 指向另一个其他类型的双精度指针。

问:

  1. 为什么我们不能 static_cast 一个双 void 指针?
  2. 投射双 void 指针的正确方法是什么?

【问题讨论】:

  • 整数类型也会出现同样的问题。您可以static_castintlong。您不能将static_castint * 转换为long *,即使在intlong 具有相同表示和对齐方式的实现上也是如此。你认为你应该能够在这些实现上做到这一点吗?如果没有,为什么不呢?
  • void** 最初是如何出现在您的代码中的?
  • @AntonSavin Legacy C 代码,我也不喜欢它,但我必须处理它:(。
  • @40two 所以这个东西是由某个 C 函数返回的,还是传递给某个函数的参数?
  • @AntonSavin 实际上是“长老”尝试创建一个通用的指针数组。

标签: c++ pointers void-pointers


【解决方案1】:

static_cast 可以执行任何隐式转换的逆操作。

存在隐式转换int*void*;它只会丢失有关指向对象类型的信息。

没有隐式转换int**void**。如果允许,它将重新解释指向的对象指针,即将int* 重新解释为void*。在一些旧架构上,int* 不一定具有像 void*(或 char*,对齐要求最少的指针类型)一样大的值表示。

重新解释需要reinterpret_cast

恕我直言,最好使用reinterpret_cast一般void* 进行转换,因为这会向读者传达意图。然而,我记得 Sutter 和 Alexandrescu 建议使用 static_cast,大概是由于 C++03 中缺乏正式的保证。我还记得,这个纯粹的形式问题在 C++11 中得到了修复。

【讨论】:

    【解决方案2】:

    我相信这是一个 XY 问题。根据具体情况,不用reinterpret_cast也可以解决。如果您知道void** 指针实际上指向了一个指向int 的指针,您可以安全地这样做:

    int* iptr = static_cast<int*>(*v_dptr);
    

    除非您的代码中确实需要int**。如果你需要,你可以这样做:

    int** i_dptr = &iptr;
    

    但请注意,它会指向一个局部变量 iptr,当超出范围时该变量将被销毁。

    【讨论】:

      【解决方案3】:

      Q1 有很好的答案,Q2 的答案很大程度上取决于意图。如果 void **v_dptr 旨在成为指向“通用”void* 的指针,您知道它实际上是 int* 并且您想要相应地进行转换,那么您可能想要以下内容:

      int *i_ptr = static_cast<int*>(*v_dptr);
      int **i_dptr = &i_ptr;
      

      【讨论】:

        【解决方案4】:

        当您拥有void* 并将其转换为int* 时,可能会或可能不会进行一些数学/宽度调整来创建正确类型的实例,static_cast&lt;&gt; 将准备这样做。当您只有一个指向void* 的指针并且想要一个指向int* 的指针时,static_cast&lt;&gt; 对指向的void* 对象没有写访问权;不能随意调整它以确保它是有效的int*,以便static_cast&lt;&gt; 可以成功并返回一个真正可用于访问有效int* 的指针。虽然在某些架构上这可能无关紧要,但如果标准允许这样做,那么代码在移植时可能会中断。 (请记住,期望static_cast&lt;&gt; 在使用static_cast&lt;int*&gt;(the_void_ptr) 初始化的某处为int* 安排一些额外的内存是不合理的——这不仅会产生意想不到的开销,而且还需要在线程特定的内存或动态分配并以某种方式释放,实际上最终比较指针值的所有代码都会中断。)

        如果您想强制执行此操作并向编译器保证两种类型的宽度相同且无需进行数学调整等 - 那么您可以使用reinterpret_cast&lt;&gt; 并控制它,明确表示您是接受未定义行为的风险。

        【讨论】:

          【解决方案5】:

          void* 的特殊之处在于它可以指向任何东西。它是“指向未指定类型的指针”。因此,对于某些类型的T,在void*T* 之间的转换是一种“正常”操作,static_cast 可以支持这种操作。实际上,这样的转换表示:“指针不知道它指向什么,但我知道:它指向一个T。”

          void** 在这种情况下并不特殊。它只能指向一件事,指向void*。将void** 转换为int** 会有所不同:“指针声称它指向void*,但我想将其视为指向int* 的指针。”如果您想将一件事视为另一件事,您需要重新解释原来的 - 所以请使用reinterpret_cast

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2023-01-29
            • 1970-01-01
            • 1970-01-01
            • 2011-05-19
            • 1970-01-01
            • 2020-12-10
            • 1970-01-01
            相关资源
            最近更新 更多