定义解引用空指针的一致行为需要编译器在大多数CPU体系结构上在每次解引用之前检查空指针。对于一个旨在追求速度的语言来说,这是一个无法承受的负担。
此外,这只修复了更大问题的一小部分 - 除了空指针之外,还有许多其他方式可以导致无效指针。
NULL
必须是特殊的,而据我所知,OP的问题更多的是,为什么它应该是特殊的? - user541686NULL
是特殊的?它在解引用方面与未初始化的指针或不再指向现有对象的指针一样不特殊。 - James McNellisNULL
指针(或其他无效指针),如果它不是特殊的指针,它会像任何其他指针一样解引用它。只有当它是特殊的指针时,编译器才需要检查。 - user541686要给出明确的行为,唯一的方法是在每个指针解引用和每个指针算术操作中添加一个运行时检查。在某些情况下,这种开销是无法接受的,并且会使C++不适用于通常使用的高性能应用程序。
C ++允许您创建自己的智能指针类型(或使用库提供的类型),在安全性比性能更重要的情况下可以包含此类检查。
在C语言中,按照C99标准第6.5.3.2 / 4条款规定,对空指针进行解引用也是未定义的。
这个 来自 @Johannes Schaub - litb 的回答提出了一个有趣的理由,看起来相当令人信服。
typeid
运算符的语义使部分痛苦得到了定义。它表示,如果给出从取消引用空指针而导致的lvalue,结果是抛出一个bad_typeid
异常。尽管如此,在存在与上述找到身份问题的例外情况下,还存在其他类似的未定义行为例外情况(尽管远不那么微妙,并且对受影响章节进行了参考)。注意:
将此标记为社区wiki,因为答案和信用应归原始发布者所有。我只是在此粘贴原始答案的相关部分。
实际问题是,你期望得到什么行为?
空指针是一种特殊的值,代表没有对象存在。解除引用指针的结果是获得指向所指对象的引用。
那么,如何从指向虚空的指针中获得好的引用呢?
你不能。因此会出现未定义的行为。
abort()
?可以定义很多明智的方法,问题是为什么要让它未定义? - Mike Seymourthrow
的情况下抛出异常是不明智的做法。(是的,你可以对Java做出结论。) - curiousguy所以,我想这就是为什么将nullptr(可能还有其他无效指针)取消引用标记为未定义的原因。
我确实想知道为什么这是未定义的,而不是未指定或实现定义的,这些与未定义的行为有所不同,但需要更多的一致性。
特别是,当程序触发未定义的行为时,编译器可以做任何事情(例如,也许丢弃整个程序?),仍然被认为是正确的,这有点棘手。在实践中,您会期望编译器只是将null指针取消引用编译为地址零的读取,但随着现代优化器变得更好,但也更加敏感于未定义的行为,我认为它们有时会做一些使程序更彻底地崩溃的事情。例如,考虑以下内容:
matthijs@grubby:~$ cat test.c
unsigned foo () {
unsigned *foo = 0;
return *foo;
}
matthijs@grubby:~$ arm-none-eabi-gcc -c test.c -Os && objdump -d test.o
test.o: file format elf32-littlearm
Disassembly of section .text:
00000000 <foo>:
0: e3a03000 mov r3, #0
4: e5933000 ldr r3, [r3]
8: e7f000f0 udf #0
我怀疑这是因为如果行为被定义清楚,编译器必须在指针被解引用的任何地方插入代码。如果它是实现定义的,则可能的一种行为仍然可能是硬崩溃。如果未指定,则某些系统的编译器可能会有额外的不当负担,或者它们可能会生成导致硬崩溃的代码。
因此,为了避免对编译器造成任何可能的额外负担,他们将该行为定义为未定义状态。
这里是一个简单的测试和示例:
分配一个指针:
int * pointer;
? 当指针被创建时,它的值是什么?
? 指针指向什么?
? 当我在其当前状态下取消引用此指针时会发生什么?
需要有一个值来指示指针没有指向任何东西或处于无效状态。这就是NULL指针概念发挥作用的地方。链表可以使用NULL指针来指示列表的末尾。
根据原始C标准,NULL可以是任何值 - 不一定是零。
语言定义规定,对于每种指针类型,都有一个特殊值 - “空指针” - 它可与所有其他指针值区分开来,并且“保证与指向任何对象或函数的指针不相等”。也就是说,空指针明确地指向无处; 它不是任何对象或函数的地址。
每种指针类型都有一个空指针,并且不同类型的空指针的内部值可能是不同的。