C++标准:解引用空指针以获取引用?

41

我想知道C++标准对于这样的代码有何规定:

int* ptr = NULL;
int& ref = *ptr;
int* ptr2 = &ref;

实际情况是,ptr2 是 NULL,但我想知道,这只是一个实现细节,还是在标准中有明确规定?
在不同的情况下,对 NULL 指针进行解引用应该会导致崩溃,但在这里,我正在将其解引用以获得由编译器实现为指针的引用,因此实际上并没有解引用 NULL。


5
该网页提供了一个非常好的答案来解决这个问题。 - pmr
5个回答

53

对空指针进行引用是未定义的行为。

实际上,标准在注释中(8.3.2/4“引用”)明确指出了这种情况:

注意:特别地,在一个良定义的程序中不存在空引用,因为创建这样的引用的唯一方法是将它绑定到通过对空指针进行引用而获得的“对象”,这会导致未定义的行为。


顺便提一下:我知道唯一可以以良定义方式“解引用”空指针的情况是作为 sizeof 运算符的操作数,因为 sizeof 的操作数实际上并没有被计算(所以永远不会真正发生解引用)。


6
如果它是typeid的操作数,且是类型为PolymorphClass*的空指针,则它将被解引用评估。尽管如此,这仍是明确定义的(引发bad_typeid异常)。我真的不喜欢那个特殊的typeid处理。 - Johannes Schaub - litb
3
不允许使用空引用确实令人遗憾。它具有一些很酷的性质,但我想它只会让代码变得更加复杂。:( 如果有前置条件可用且代码可以更好地验证,那么就可以安全地使用空引用。 - Adrian
1
sizeof() 是一个作用于变量类型而非值的运算符。空指针确实有类型。 - jforberg
@jforberg:我没有看到这里有人声称相反? - Lightness Races in Orbit
1
@Lightness 没有人是。我只是在澄清。 - jforberg

5
在C++标准中,对空指针进行解引用明确是未定义行为,因此你所看到的是实现特定的行为。
从C++0x草案标准中的1.9.4复制(在这方面类似于先前的标准):
特定的其他操作被描述为未定义(例如,解引用空指针的效果)。[注意:本国际标准对包含未定义行为的程序的行为不施加任何要求。-注]

3
有趣的是,因为没有人能够展示在标准中明确规定对空指针进行解引用会导致未定义行为。在这里,标准是错误的。 - curiousguy
2
@curiousguy:8.3.2.5 再次提到对空指针进行解引用是未定义的事实。但你是正确的,它确实像在其他地方解释过一样。无论如何,1.3.13 解释了任何未明确定义的行为都被视为未定义,因此这可能是情况。 - Gorpik
3
"8.3.2/5" 顺便提一下,它的拼写是8.3.2/5。没有8.3.2.5这个部分。"任何未明确定义的行为都被视为未定义" 您是正确的:仅当指针指向对象时,解引用指针才被定义。但如果标准明确说明这一点,那将会更清晰一些,并且在这个非问题上将会少花费一些电子墨水。 - curiousguy
4
注意事项不是规范性的。实际上,根据DR 1102的结果,此注释已被删除,理由是对空指针进行解引用并不是未定义行为。 - M.M

1

解除引用空指针是未定义的行为。在解除引用之前,您应该检查值是否为NULL。


1

哦,非常有创意的想法。所以,允许使用空的“this”调用成员函数!!!我想知道他们在吸什么烟。无论如何,7年后,在这种疯狂的方向上什么也没有做。 - curiousguy
实际上并不是这样的,根据 https://dev59.com/u3E95IYBdhLWcg3wEpro#2474021 的解释(因为涉及到左值到右值的转换) - Kos
有趣的是,#232使用除以零作为UB的示例,而许多C++实现隐含地包含IEEE-754,该标准定义了正零和负零的除法效果。 整数除以零是UB,但该示例没有指定。 - supercat
你必须使用单词“许多”是一个UB的例子。 - Jeremy

-2
int& ref = *ptr;

上述语句实际上并没有解除引用任何内容。因此,在使用 ref(无效)之前是没有问题的。


4
废话。一元运算符*实际上是解引用运算符,它真正执行的是解引用操作。 - curiousguy
1
@curiousguy:我建议你编译并在反汇编器中查看代码。引用“ref”本质上是一个指针,因此上述语句实际上是在幕后进行指针赋值。 - valdo
2
你误解了我的意思。我不在乎生成的汇编代码是什么样子的。根据你的理解,*ptr是什么? - curiousguy
1
@curiousguy:*ptr确实是一个解引用。但这并不意味着它实际上被执行(评估)。例如,您可以编写sizeof(*ptr),表达式*ptr在运行时保证不会被评估。 - valdo
4
的确,sizeof 的操作数不会被求值:你可以毫无问题地写下 sizeof(1./0.)。这是因为 sizeof 只关心其操作数的类型。另一方面,& 的操作数会被求值,否则“地址运算符”应用于什么呢? - curiousguy
我可能存在错误的理解:根据输出汇编,这(在实践中)可能不是一个取消引用,因此仅从这一行不会看到segfault或valgrind错误。但是,根据编译器对未定义行为的理解,它确实是一个取消引用,因此如果编译器选择这样做,它可以输出完全错误的程序。 - Jack O'Connor

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