【问题标题】:Why is static_cast of void* to another type allowed?为什么允许将 void* 的 static_cast 转换为另一种类型?
【发布时间】:2013-07-07 16:40:36
【问题描述】:

我刚刚读到这个帖子: Simple c++ pointer casting

这让我开始思考为什么不允许不同指针类型之间的 static_cast(除非在这种情况下)除非您将 static_cast 转换为 void* 作为中间步骤。在我看来,两者都应该被允许,或者都不应该被允许。这是一个例子:

char*          cs;
unsigned char* ucs;

cs = reinterpret_cast<char*>(ucs);                  // 1) allowed, of course
cs = static_cast<char*>(ucs);                       // 2) not allowed: incompatible pointer types
cs = static_cast<char*>( static_cast<void*>(ucs) ); // 3) now it's allowed!

在我看来,如果 #3 是可能的,那么 #2 也应该被允许。或者相反,如果由于指针不兼容(需要 reinterpret_cast)而不允许使用 #2,那么基于指针不兼容的原因,可能不允许 static_casting from void* 到任何东西。 (当然,将任何其他指针 转换为 void* 总是可以的。)

那么为什么其中一种可能性不是真的——#2 和#3 要么都被允许,要么都不被允许?为什么它会像我的示例中所示那样工作?

【问题讨论】:

  • 因为 C++ 可以防止意外,而不是防止滥用。并且 C++ 重视安全性。
  • 当然,您可以执行const int i = 0; *const_cast&lt;int *&gt;(&amp;i) = 1; 并且它会编译,但它违反了约束并导致 UB。这就是void * 的精彩之处。 C++ 显然没有设法完全消除 C 的类型不安全性。
  • @H2CO3 当然,但这就是 const_cast 所针对的那种不安全。同样,unsigned char*char* (显然)是那种不安全的 reinterpret_cast 是为了(不是 static_cast)......所以我想我的意思是,如果这对于 static_cast 来说“太不安全”,那么为什么不投射void* 也需要 reinterpret_cast 吗?
  • @DaveLillethun 因为void * 旨在与任何(数据)指针类型兼容。
  • @DaveLillethun:有一个特定的用例有效:从T* 转换为void* 并返回T*,除此之外还有其他要与之交互的事情遗留的 C 风格 API。现在,一旦编译器得到void*,它就不可能知道它是来自char* 还是unsigned char*,因此不需要诊断问题。

标签: c++ pointers casting void-pointers static-cast


【解决方案1】:

好的,我将在这里尝试回答我自己的问题,因为我想我有一个想法,尽管我不确定它是否正确。我猜,如果您认为我通过对我的答案投票赞成/反对,您可以告诉我。 :)

所以我认为static_cast 是在告诉编译器,“相信我,这是安全的。”而reinterpret_cast 是在告诉编译器,“我知道这是不安全的;无论如何都要这样做。”

回到我的例子:

cs = reinterpret_cast<char*>(ucs);

是不安全的,但编译器还是会这样做,因为你告诉它这样做。

cs = static_cast<char*>(ucs);

将导致编译器错误,因为您说“相信我,这是安全的”,但编译器知道它确实不是。所以它不信任你,因为在这种情况下它实际上知道,因此会出错。

现在最后一个例子有两部分:

void* temp = static_cast<void*>(ucs);

当然是允许的,因为将 转换为 void* 是安全的。

cs = static_case<char*>(temp); // remember temp is a void*

是允许的,因为将 from 强制转换为 void* 有时是安全的,有时不是 - 编译器无法判断它在任何给定实例中是什么......所以当你说,“相信我,这是安全,”编译器信任你——如果你错了,那就是你的问题。 O.O

所以换句话说,你不能 static_cast 一些永远不会安全的东西 - 那是你需要 reinterpret_cast 的时候......但是你可以 static_cast 一些有时安全并且有时不是(并且编译器无法判断任何给定实例中的情况)。

说了这么多...我在这里提到了很多“安全”和“不安全”,但这些词的实际含义是什么? (所以,在某种程度上,我什至不明白我自己的答案......!如果你能帮我弄清楚,请发表评论......)

【讨论】:

  • 这并不完全正确。将signed char* 转换为unsigned char*char*安全性 可以基于不同的理由进行争论,但该标准明确允许对指向不同类型的指针进行别名处理,这些类型仅在符号性上有所不同,因此它确实提供有关该特定转换的保证,尽管它不允许您执行static_cast。另请注意,static_cast 允许进行许多不安全的转换。我倾向于从开发人员的角度更多地看待这一点。演员表是一个警告信号。 reinterpret_cast 是带有频闪灯的警告标志。
  • 好吧,那么整个事情就是任意的!
【解决方案2】:

问题是这样的:

my_type *p = new my_type;
void *vp = p; // okay

现在,如果您需要取回原始指针,请执行以下操作:

my_type *new_p = static_cast<my_type*>(vp); // okay

很好,结果定义明确。如果您使用static_cast 将指针转换为与原始指针不同的不同 类型,则会出现未定义的行为,而这通常是编译器无法检测到的。所以演员表是允许的,如果你滥用它,它就在你的头上。

【讨论】:

  • 我明白你在说什么,但我还是不明白为什么它不需要你做 my_type *new_p = reinterpret_cast&lt;my_type*&gt;(vp); 而不是 static_cast...
  • reinterpret_cast 用于时髦的转换。 T* -&gt; void* -&gt; T* 很常见,而且一点也不时髦。也就是说,reinterpret_cast 经常进行不可移植的转换,应该是一个需要仔细审查的标志。
  • 你能定义“时髦”吗?我的直觉反应是,改变符号也不是“时髦”……但这是基于一种不精确的感觉,而不是对这个词的明确定义。
【解决方案3】:
cs = static_cast<char*>( static_cast<void*>(ucs) ); // 3) now it's allowed!

它会编译。这是否意味着它是允许的?不是。该标准仅在 void* 是通过从 T* 转换获得的情况下才允许从 void* 转换为 T*,这不是您的情况。

【讨论】:

  • 也许你可以稍微详细说明一下如果你做演员会发生什么,不管标准中的限制如何...... :)
  • 你能澄清一下“允许”吗?这会引发运行时错误吗?
  • 这是未定义的行为,理论上任何事情都可能发生。在实践中,在您的特定情况下,它很可能出于两个原因起作用。首先是类型仅在符号上有所不同。二是char*是特殊的,在标准中有特殊处理。无论如何,static_cast 的序列确实是不允许的,应该使用reinterpret_cast 完成转换。
  • 我认为未定义的行为和不允许的行为不是一回事,实际上是相互排斥的。也就是说,我不同意这是一个坏主意,reinterpret_cast 确实是需要的......但我想这是我问题的症结所在 - 如果这是一个坏主意,为什么不@ void* 到任何其他指针类型的所有转换也需要 987654330@?
  • @underscore_d:从 void* 到 C++11 static_cast&lt;&gt; 仅限于转换回相同类型。我已经验证了在 C++14 和 static_cast 中的措辞已经改变,并且只要对齐要求相同,它确实允许从 void* 转换回非原始类型。
猜你喜欢
  • 2015-03-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-09
  • 2012-03-14
  • 2012-03-28
  • 2020-06-24
相关资源
最近更新 更多