【问题标题】:Does upcasting a null pointer lead to undefined behavior向上转换空指针是否会导致未定义的行为
【发布时间】:2015-04-24 14:53:24
【问题描述】:

我想知道以下代码是否会导致未定义的行为:

#include <cstddef>
#include <cstdio>

struct IA { 
  virtual ~IA() {}
  int a = 0;
};
struct IB {
  virtual ~IB() {}
  int b = 0;
};
struct C: IA, IB {};

int main() {
  C* pc = nullptr;
  IB* pib = pc;
  std::printf("%p %p", (void*)pc, (void*)pib);
}

【问题讨论】:

  • 这会使用 c++ 14 生成 0 0ideone.com/iefRnb
  • 具有多重继承(以及因此指针调整)的示例会更有趣。
  • @Quentin 同意。将更新问题。

标签: c++ language-lawyer implicit-conversion nullptr upcasting


【解决方案1】:

向上转换一个空指针被定义为给你另一个空指针:

4.10p3:

“指向cvD”类型的纯右值,其中D 是类类型,可以转换为“指向cv的指针”类型的纯右值> B",其中BD 的基类。 ... 将空指针值转换为目标类型的空指针值。

【讨论】:

    【解决方案2】:

    Stroustrup 在his 1989 multiple inheritance paper [PDF] 的第 4.5 节中讨论了这个案例:

    解决方案是详细说明转换(强制转换)操作为 测试指针值 0 [...]

    增加的复杂性和运行时开销是一个测试和一个 递增。

    实现显式检查空值并确保强制转换的结果仍然是空值。这在 C++98 中是正确的,并且在 C++11 和 nullptr 中没有改变。

    这在多个基类的情况下尤其重要,从派生类到其中一个基类的强制转换可能需要更改指针的实际值。

    在您的示例中,内存中C 的布局将首先包含IA 的字节,然后是IB 的字节。强制转换为IA 很简单,因为指向C 开头的指针也将指向CIA 部分的开头。另一方面,转换为IB 需要将C 指针移动IA 的大小。在 nullptr 情况下执行此转换将导致强制转换后的非空指针,因此对空值进行特殊处理。

    作为pointed out by aschepler,标准中的相关部分是[conv.ptr] §4.10:

    类型为“pointer to cv D”的纯右值,其中D是一个类类型,可以是 转换为“指向 cv B 的指针”类型的纯右值,其中 B 是基数 D 的类 [...]。 [...] 转换的结果是一个指向 派生类对象的基类子对象。空指针 value 被转换为目标类型的空指针值。

    【讨论】:

    • 您的回答为实施提供了宝贵的见解。干得好!
    • @Lingxi 谢谢 :) 我真的很喜欢 Stroustrup 的那篇论文。它对于理解编译器实际上如何处理底层的多重继承非常有用。
    • 如果您可以参考标准更新您的答案(就像@aschepler 所做的那样),那将是最好的。了解迂腐的标准和一些实际的实施问题是很好的。
    猜你喜欢
    • 2019-06-03
    • 2014-06-10
    • 1970-01-01
    • 2020-09-07
    • 2017-01-30
    • 1970-01-01
    • 2017-06-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多