答案取决于你的观点:
如果您按照 C++ 标准进行判断,您将无法获得空引用,因为您首先会获得未定义的行为。在第一次发生未定义行为之后,该标准允许任何事情发生。因此,如果您编写*(int*)0
,从语言标准的角度来看,您已经具有未定义的行为,即取消引用空指针。程序的其余部分无关紧要,一旦执行此表达式,您将退出游戏。
但是,在实践中,可以很容易地从空指针创建空引用,并且在您实际尝试访问空引用后面的值之前您不会注意到。您的示例可能有点太简单了,因为任何好的优化编译器都会看到未定义的行为,并简单地优化掉任何依赖它的东西(甚至不会创建空引用,它会被优化掉)。
然而,优化依赖于编译器来证明未定义的行为,这可能是不可能的。考虑文件中的这个简单函数converter.cpp
:
int& toReference(int* pointer) {
return *pointer;
}
当编译器看到这个函数时,它不知道指针是否为空指针。所以它只是生成将任何指针转换为相应引用的代码。(顺便说一句:这是一个 noop,因为指针和引用在汇编程序中是完全相同的野兽。)现在,如果您有另一个user.cpp
包含代码的文件
#include "converter.h"
void foo() {
int& nullRef = toReference(nullptr);
cout << nullRef; //crash happens here
}
编译器不知道这toReference()
将取消引用传递的指针,并假设它返回一个有效的引用,这在实践中碰巧是一个空引用。调用成功,但当您尝试使用引用时,程序崩溃。希望。该标准允许任何事情发生,包括粉红色大象的出现。
你可能会问为什么这是相关的,毕竟未定义的行为已经在内部触发了toReference()
。答案是调试:空引用可以像空指针一样传播和增殖。如果您不知道可能存在空引用,并学会避免创建它们,您可能会花费相当长的时间试图弄清楚为什么您的成员函数在尝试读取普通旧成员时似乎崩溃int
(答案:实例在成员的调用中是空引用,this
空指针也是如此,并且您的成员被计算为位于地址 8)。
那么如何检查空引用呢?你给线了
if( & nullReference == 0 ) // null reference
在你的问题中。好吧,那是行不通的:根据标准,如果取消引用空指针,则具有未定义的行为,并且如果不取消引用空指针就无法创建空引用,因此空引用仅存在于未定义行为的范围内。由于您的编译器可能假设您没有触发未定义的行为,因此它可以假设不存在空引用之类的东西(即使它很容易发出生成空引用的代码!)。因此,它看到了if()
条件,得出结论它不可能是真的,然后扔掉整个if()
语句。随着链接时间优化的引入,以稳健的方式检查空引用变得完全不可能。
TL;博士:
空引用有点可怕:
它们的存在似乎是不可能的(= 按标准),
但它们存在(= 通过生成的机器代码),
但是如果它们存在,您将看不到它们(= 您的尝试将被优化掉),
但它们可能会在不知情的情况下杀死您(=你的程序在奇怪的地方崩溃,或者更糟)。
您唯一的希望是它们不存在(= 编写程序以不创建它们)。
我希望这不会困扰你!