【问题标题】:Why Simply de-referencing a pointer with NULL assigned is not crashing [duplicate]为什么简单地取消引用分配了 NULL 的指针不会崩溃 [重复]
【发布时间】:2016-08-21 11:37:13
【问题描述】:
 #include <iostream>
 int main()
 {
    int *ptr = NULL;
    // It does not crash
    *ptr; --------> Point-1
    //But this statment crashed
    std::cout<<"Null:"<<*ptr<<"\n"; ------> Point-2
    return 0;
 }

在上面的代码中,当我评论“Point-2”时,代码没有崩溃。 但是当我取消注释“Point-2”时,它就崩溃了。 由于 ptr 理想情况下为 NULL,因此 Point-1 也应该崩溃。如果我错了,请纠正我。 有人能解释一下为什么当我简单地取消引用指针时代码没有崩溃吗?

【问题讨论】:

  • 在没有优化的调试模式下编译,然后重试..
  • 停止假设调用未定义的行为会导致已定义的行为。它没有。事实是,如果你幸运,它就会崩溃,而你真的很想看看它以了解为什么会这样。
  • 我们需要一个规范的问题“为什么调用未定义行为的代码的行为方式让我感到惊讶?”回答“因为它可以。”
  • @WhozCraig 在这种情况下编译器可以抛出一个没有副作用的未使用表达式的原因是因为取消引用 NULL 是 UB。否则,该表达式可能会产生副作用。 (这也是取消引用 NULL 是 UB 的原因。我们希望这种优化成为可能。)

标签: c++ undefined-behavior


【解决方案1】:

取消引用空指针是未定义的行为。未定义的行为不等于错误。如果您触发了未定义的行为,任何事情都可能发生。如果你问的是另一种方式:

为什么未定义的行为不会导致错误,而是给我们带来奇怪的行为?

这可能是由于多种原因。一个原因是性能。例如,在std::vector 的实现中(至少在MSVC 中),在Release Mode 中没有检查索引是否超出范围。您可以尝试这样做:

std::vector<int> v(4);
v[4]=0;

它将编译并运行。你可能有奇怪的行为,也可能没有。但是,在调试模式下,它会在运行时抛出异常。 MSVC 在调试模式下进行检查,因为在调试模式下性能并不重要。但它不在发布模式下,因为性能很重要。

同样适用于取消引用空指针。你可以想象解引用的代码将被放在这样的包装器中:

//Imaginary code
T& dereference(T* ptr){
    if(ptr==nullptr){
        throw;
    }
    return *ptr;
}

这部分:if(ptr==nullptr){throw;} 将减慢上下文中每个指针的解引用过程,这是不可取的。

不过,也可以这样:

//Imaginary code
T& dereference(T* ptr){
    #ifdef DEBUG
    if(ptr==nullptr){
        throw;
    }
    #endif
    return *ptr;
}

我想你现在明白了。

【讨论】:

  • 我将第二句话表述为“未定义的行为不等于崩溃”(它一个错误 - 不一定是被捕获的错误)。跨度>
  • 虽然你说的是真的,但我认为在这种特殊情况下,访问没有发生,因为它正在被优化。
  • @MartinBonner mmm 我真的不知道.. 是错误,根据定义,有什么不正确的吗?至少不应该触发它吗?我不是说抓住了,只是触发了?未定义的行为可能不会触发任何事情。对吗?
  • 是的,我想是的..我的回答一般..随时用这个东西编辑答案,或者你可以发布新的答案
【解决方案2】:

在第 2 点中,您尝试显示 0x0 地址的内容,这会产生访问冲突错误。

在第 1 点中,您什么也不做,因此程序不必访问此指针所描述的内存。这不会产生访问冲突错误。

【讨论】:

  • 不明白为什么这被否决了。我认为这是完全正确的。
  • @MartinBonner 它没有解释为什么程序不必访问内存。例如,如果取消引用 NULL 指针具有某些已定义的行为,则通过删除访问进行优化将是非法的(因为您不会获得您应该获得的行为)。因为是UB才可以去掉访问权限。
  • @DavidSchwartz:这是错误的。可以优化访问,因为取消引用指向非volatile 的指针没有副作用。和UB完全没有关系。如果代码是int i=42, *ptr=&amp;i; *ptr;,编译器仍然可以优化访问。
  • @MartinBonner 对,因为它是 UB。例如,如果取消引用 NULL 肯定会导致崩溃,那么它会产生副作用,即崩溃,因此无法优化。这是因为取消引用 NULL 是没有副作用的 UB。 (同样取消引用其他无效指针是 UB。)
猜你喜欢
  • 2017-12-25
  • 2021-08-06
  • 2018-03-23
  • 2020-05-12
  • 2021-05-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多