空引用的崩溃行为

时间:2014-07-17 04:07:59

标签: c++ reference nullreferenceexception

当糟糕的C ++代码尝试创建如下所示的空引用时:

int &ptr2ref(int *p){
    return *p;
}

int calc(int &v){
    return v*2;
}

...
int &i = ptr2ref(nullptr);
calc(i);

至少在Visual C ++中,它在函数calc的返回语句中崩溃了(调试模式)。

然而,this question引用的答案

8.3.2 / 1:

  

引用应初始化为   引用有效的对象或功能。   [注意:特别是空引用   不能存在于明确的定义中   程序,因为唯一的方法   创造这样的参考将是   将它绑定到由...获得的“对象”   取消引用空指针   导致未定义的行为。如   在9.6中描述,参考不能   直接绑定到一个位域。 ]

1.9 / 4:

  

描述了某些其他操作   在本国际标准中   undefined(例如,。的效果)   取消引用空指针)

如果我理解正确,标准说只要创建一个空引用,程序行为就是未定义的。 因此,如果编译器打算生成有用的调试信息,它应该在上面的示例中的函数ptr2ref崩溃,因为存在创建空引用(以及引用发生)的地方。

我错过了什么吗?是否有任何问题导致编译器在最低调试模式生成此类代码

未定义的行为

我知道人们会争辩说" undefined"大致意味着一切。我的论点是,鉴于标准没有规定简单的int main(){}需要多长时间来编译,没有人会接受编译时间超过一天。所以问题在于实施选项,而不是标准本身。 我引用此标准只是说ptr2ref上的崩溃是一个选项

此外,在调试模式下已经发生了很多额外的检查,例如,在从函数返回之前,始终检查堆栈以查看是否存在任何损坏。与那些我认为在调试模式中添加一个相对简单的检查会过于宽泛的比较。

3 个答案:

答案 0 :(得分:4)

“未定义的行为”并不意味着“立即崩溃”。

它在C ++标准的1.3.24节中定义

  

本国际标准没有要求的行为

     

[注意:本国际时可能会出现未定义的行为   标准省略了行为或程序的任何明确定义   使用错误的构造或错误的数据。允许的未定义   行为范围从完全忽略这种情况开始   不可预知的结果,在翻译或程序期间表现   以文件化的方式执行环境特征   (有或没有发出诊断信息),终止   翻译或执行(发布诊断   消息)即可。许多错误的程序结构不会产生未定义的   行为;他们需要被诊断出来。

  • 绑定空引用后,程序不需要立即崩溃。
  • 让编译器生成代码来检查这些情况会给程序带来巨大的开销,这是不可接受的。

答案 1 :(得分:1)

“未定义的行为”意味着任何事情都可能发生,并且编译器没有义务做任何特定的事情。在这种情况下,空指针解除引用没有发生灾难性事件,它只是通过创建空引用使程序处于无效状态。这会在以后引起问题。

当然,如果可以更早地检测到错误,那将是可取的,但是这样做的唯一方法是编译器将明确的空指针检查添加到所有解除引用操作,这只会浪费性能良好的性能(没有使用错误的空指针)程序。由于空指针解除引用通常很快就会导致崩溃,所以即使在调试模式下也可能不值得这样做。

答案 2 :(得分:1)

  

如果我理解正确,标准说只要创建一个空引用,程序行为就是未定义的。

是的,这是正确的。确保这一点的文本是:

  

应初始化引用以引用有效的对象或函数。

取消引用空指针的结果肯定不是有效的对象或函数。

您还引用以下文字:

  

[注意:特别是,在一个定义良好的程序中不能存在空引用,因为创建这样一个引用的唯一方法是将它绑定到通过解除引用空指针获得的“对象”,这会导致未定义行为。如9.6中所述,引用不能直接绑定到位字段。 ]

但是,“注意”意味着它是非规范,即文本是解释性的,但实际上并不构成标准规范的一部分。而且,有些令人惊讶的是,事实证明,标准实际上并没有说明(我知道)*p导致未定义的行为。

确实表示*p上的左值到右值转换会导致未定义的行为,但它也表示在绑定引用的情况下不会执行此转换。

这出现在issue 1102