为什么将指针赋值为NULL后,简单地取消引用指针不会崩溃

3
 #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为空指针,理论上Point-1也应该崩溃。如果我有错,请纠正我。有人能解释一下为什么只是简单地解引用指针时代码没有崩溃吗?

2
以调试模式编译,不进行任何优化,然后再尝试一次。 - undefined
2
不要假设调用未定义行为会导致定义行为。事实并非如此。事实上,如果你幸运的话,它会崩溃,而你真的必须很想看到它才能理解为什么是这样的。 - undefined
2
供您参考:- https://dev59.com/eGcs5IYBdhLWcg3wtmED - undefined
1
我们需要一个规范问题:“为什么调用未定义行为的代码会以让我感到惊讶的方式运行?”并附上答案:“因为它可以这样。” - undefined
1
@WhozCraig 在这种情况下,编译器可以丢弃没有副作用的未使用表达式的原因是因为解引用 NULL 是未定义行为。如果不是这样,该表达式可能会产生副作用。(这也是解引用 NULL 是未定义行为的原因。我们希望这种优化是可能的。) - undefined
显示剩余5条评论
2个回答

2
空指针解引用是未定义行为。未定义行为不等于错误。如果您触发了未定义行为,任何事情都可能发生。如果您反过来问:

为什么未定义行为不会引起错误,而是给我们奇怪的行为?

这可能有很多原因。一个原因是性能。例如,在std::vector的实现中(至少在MSVC中),在发布模式下没有检查索引是否越界。您可以尝试这样做:
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;
}

我想你现在已经有了主意。

我会将第二句话表述为“未定义行为并不等同于崩溃”(它是一个错误 - 只是不一定会被捕获的错误)。 - undefined
虽然你说的没错,但我认为在这个特定情况下,访问没有发生是因为它被优化掉了。 - undefined
@MartinBonner 嗯,我真的不知道... 错误是指某些东西不正确吗?它至少应该被触发才对吧?我并不是说要捕获错误,只是触发一下而已。未定义行为可能不会触发任何事情,对吗? - undefined
是的,我认为是这样。我回答比较笼统。你可以随意编辑这个答案或者你可以发布一个新答案。 - undefined

0
在第2点中,您尝试显示0x0地址的内容,这会生成访问冲突错误。
在第1点中,您对其不做任何操作,因此程序不必访问由该指针描述的内存。这不会生成任何访问冲突错误。

不明白为什么这被点踩了。我觉得它完全正确。 - undefined
@MartinBonner 这并没有解释为什么程序不需要访问内存。举个例子,如果对空指针进行解引用有一些定义好的行为,通过删除访问来进行优化将是非法的(因为你将无法获得预期的行为)。正是因为存在未定义行为,所以可以删除该访问。 - undefined
1
@DavidSchwartz:那是错误的。因为对非“volatile”指针进行解引用没有副作用,所以可以优化访问。这与UB无关。如果代码是int i=42, *ptr=&i; *ptr;,编译器仍然可以优化访问。 - undefined
@MartinBonner 对的,因为这是未定义行为。例如,如果保证解引用一个空指针会导致崩溃,那么它就会有副作用,即崩溃,因此无法被优化掉。正是因为解引用空指针是未定义行为,所以没有副作用。(同样,解引用其他无效指针也是未定义行为。) - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接