为什么地址零用于空指针?


121

在C(或C ++)中,如果指针的值为零,则它是特殊的:建议释放它们的内存后将指针设置为零,因为这意味着再次释放该指针并不危险;当我调用malloc时,如果无法获取内存,它将返回一个值为零的指针;我if (p != 0)一直在确保传递的指针有效,等等。

但是由于内存寻址从0开始,难道不是0像其他地址一样有效吗?在这种情况下,如何使用0来处理空指针?为什么负数不为null?


编辑:

一堆好答案。我将总结一下我的内心理解后所表达的答案中的内容,并希望如果我误会,社区会纠正我。

  • 像编程中的所有其他内容一样,它是一种抽象。只是一个常数,与地址0无关。C++ 0x通过添加关键字来强调这一点nullptr

  • 它甚至不是地址抽象,它是C标准所指定的常数,并且编译器可以将其转换为其他数字,只要确保它永远不等于“真实”地址即可,如果0不等于,则等于其他空指针。平台使用的最佳价值。

  • 如果不是早期的抽象,那么系统将使用地址0,而程序员无法使用该地址。

  • 我承认,我的负面建议是有点疯狂的头脑风暴。如果对地址使用带符号的整数会造成一点浪费,这意味着除了空指针(-1或其他值)以外,值空间在构成有效地址的正整数和刚刚浪费的负数之间平均分配。

  • 如果任何数字始终可以用数据类型表示,那么它就是0。(可能也是1。我认为一位整数(如果是无符号的,则为0或1),或者如果签名则只是有符号的位,或者是两位整数将会是[-2,1]。但是您可以将0设为null,将1设为内存中唯一可访问的字节。)

我脑子里仍然有一些未解决的事情。指向特定固定地址的堆栈溢出问题指针告诉我,即使空指针的0为抽象,也不一定需要其他指针值。这导致我发布了另一个堆栈溢出问题,我是否想访问地址零?


11
您可以轻松地if (p != 0)if (p)其更改为C和C ++中的常见用法,尽管如果您使用Java,则必须摆脱这种习惯。
保罗·汤布林

14
删除两次意味着您的代码是错误的。我建议不要在之后将指针设置为null,这样您就可以崩溃并解决问题,而不是抑制它。无论如何,您都会错误地假定一个地址是某个整数。这不一定是正确的,0只是代表一些特定于实现的实际指针值。从概念上讲,“否定地址”是没有意义的。
GManNickG 2010年

6
@GMan:甚至将指针设置为一个将导致崩溃的地址的好主意,例如0xDEADBEEF
Billy ONeal,2010年

5
永远不会消失的问题!

8
@Noah:该点设置为null->隐藏编程错误,请勿设置为null->查找编程错误。我不认识你,但我想让我的代码正确无误。
GManNickG

Answers:


65

2分:

  • 源代码中只有常数值0是空指针-编译器实现可以在运行代码中使用它想要或需要的任何值。某些平台具有特殊的指针值,该值“无效”,实现可能将其用作空指针。C FAQ有一个问题,“严重的是,是否有任何实际的机器确实使用了非零的空指针,或者对不同类型的指针使用了不同的表示形式?” ,指出了几个使用此属性0的平台作为C源中的空指针,同时在运行时表示不同的平台。C ++标准有一条注释,该注释清楚地表明,转换“值为零的整数常量表达式始终会产生空指针,

  • 负值在平台上可能与地址一样可用-C标准只需要选择一些东西来指示空指针,然后选择零。老实说,我不确定是否考虑过其他哨兵价值观。

空指针的唯一要求是:

  • 保证将不等于与实际对象的指针进行比较
  • 任何两个空指针都将比较相等(C ++对此进行了细化,从而只需要保留指向相同类型的指针)

12
+1我怀疑选择0仅出于历史原因。(大多数情况下,0是一个开始且无效的地址。)当然,通常这种假设并不总是正确的,但是0可以很好地工作。
GManNickG

8
空间可能也是一个促成因素。在最初开发C的时代,内存比现在昂贵得多。零数字可以使用XOR指令方便地计算,或者无需加载立即数。根据体系结构,这可能会节省空间。
闪亮的

6
@GMan-你是正确的。在早期的CPU上,内存地址零是特殊的,并且具有防止运行软件访问的硬件保护(在某些情况下,这是复位向量的开始,对其进行修改可能会阻止CPU复位或启动)。程序员使用这种硬件保护作为其软件中错误检测的一种形式,使CPU的地址解码逻辑可以检查未初始化或无效的指针,而不必花费CPU指令来执行此操作。即使地址0的目的可能已经改变,约定仍然保留到今天。
bta 2010年

10
Minix 16位编译器使用0xFFFF表示NULL。
约书亚

3
在许多嵌入式系统中,0是有效地址。值-1(所有位均为1)也是有效地址。当数据从地址0开始时,很难计算出ROM的校验和。:-(
Thomas Matthews 2010年

31

从历史上看,从0开始的地址空间始终是ROM,用于某些操作系统或低级中断处理例程,如今,由于所有内容都是虚拟的(包括地址空间),因此操作系统可以将任何分配映射到任何地址,因此它可以特别是不要在地址0分配任何东西。


6
差不多了。按照历史惯例,第一个地址用于中断处理程序,因此不能用于常规程序。同样,0是“空”,可以解释为无值/无指针。
TomTom,2010年

15

IIRC,“空指针”值不保证为零。编译器将0转换为适合系统的任何“空”值(实际上,该值始终为零,但不一定)。每当您将指针与零进行比较时,都会应用相同的转换。因为只能将指针相互比较,并且只能与此特殊值0比较指针,所以它使程序员不了解有关系统内存表示的任何信息。至于为什么选择0而不是42或类似的数字,我想这是因为大多数程序员都从0开始计数:)(而且,在大多数系统上,0是第一个内存地址,他们希望它很方便,因为在像我描述的那样练习翻译实际上很少发生;这种语言允许这样做。


5
@贾斯汀:你误会了。常数0 始终是空指针。@meador的意思是,空指针(由常数0表示)可能不对应于地址零。在某些平台上,创建空指针(int* p = 0)可能会创建一个包含该值0xdeadbeef或它喜欢的其他值的指针。0是空指针,但空指针不一定是指向地址零的指针。:)
jalf

NULL指针是保留值,取决于编译器,可以是任何位模式。NULL指针并不意味着它指向的地址0
Sharjeel阿齐兹

3
但是@Jalf,常数0 并不总是空指针。这是我们要编译器为我们填写平台的实际空指针时编写的内容。实际上,空指针通常确实对应于地址零,我将乔尔的问题解释为是为什么。毕竟,在该地址处应该有一个有效的内存字节,那么为什么不使用不存在的字节的不存在地址而不是从播放中删除有效字节呢?(我正在写我想象中的乔尔在想什么,不是我在问自己一个问题。)
罗伯·肯尼迪

@Rob:有点。我知道您的意思,您是正确的,但我也是。:)常量整数0表示源代码级别的空指针。将空指针与0进行比较会得出true。为指针分配0会将指针设置为null。0 空指针。但是,空指针的实际内存中表示形式可能与零位模式不同。(无论如何,我的意见是响应@贾斯汀的现在删除评论,不@乔尔的问题:)
jalf

@jalf @Rob,我认为您需要一些术语进行澄清。:)从§4.10/ 1开始:“ 空指针常量是整数类型的整数常量表达式右值,其值为零。可以将空指针常量转换为指针类型;结果是该类型的空指针值,并且可以与指向对象的指针或指向函数类型的指针的所有其他值区分开。”
GManNickG

15

您一定会误解指针上下文中常量零的含义。

在C和C ++指针中,“值都不能为零”。指针不是算术对象。它们不具有诸如“零”或“负”之类的数值或任何类似性质的数值。因此,您关于“指针...的值为零”的说法完全没有道理。

在C&C ++中,指针可以具有保留的空指针值。空指针值的实际表示与任何“零”无关。对于给定的平台,绝对可以是任何合适的东西。的确,在大多数平台上,空指针值实际上是由实际的零地址值表示的。但是,如果在某个平台上将地址0实际用于某些目的(即,您可能需要在地址0处创建对象),则该平台上的空指针值很可能会有所不同。例如,它可以物理上表示为0xFFFFFFFF地址值或0xBAADBAAD地址值。

但是,无论空指针值在给定平台上如何表示,在代码中,您仍将继续用constant来指定空指针0。为了将空指针值分配给给定的指针,您将继续使用p = 0。编译器有责任实现所需的内容,并将其转换为正确的空指针值表示形式,例如,将其转换为将其地址值0xFFFFFFFF放入指针的代码p

简而言之,您0在源代码中使用生成空指针值的事实并不意味着该空指针值以某种方式绑定到address 0。将0您在源代码中使用的仅仅是“语法糖”是绝对没有关系的空指针值“指点”实际的物理地址。


3
<quote>指针不是算术对象</ quote>指针算术在C和C ++中定义得很好。要求的一部分是两个指针都指向同一个组合。空指针不指向任何组合,因此在指针算术表达式中使用它是非法的。例如,不能保证(p1 - nullptr) - (p2 - nullptr) == (p1 - p2)
Ben Voigt,2010年

5
@Ben Voigt:语言规范定义了算术类型的概念。我只是说指针类型不属于算术类型的范畴。指针算术是一个完全不同的故事,仅仅是语言上的巧合。
AnT

1
某人阅读算术对象应该如何知道它是“算术类型意义上的”而不是“算术运算符意义上的”(其中的几个可在指针上使用)或“指针算术意义上的”。就语言上的巧合而言,与算术类型相比,算术对象指针算术具有更多的字母相同。同时,该标准确实讨论了指针值。原来的海报可能是指整数指针的表现,而不是指针值,并明确不需要由0来表示NULL
本·福格特

好吧,例如,C / C ++术语中的标量对象一词只是标量类型的对象的简写(就像POD对象 = POD类型的对象一样)。我以完全相同的方式使用术语算术对象,即算术类型的对象。我希望“某人”能那样理解。谁不总是可以要求澄清。
AnT

1
我在一个系统上工作(就硬件而言),null是0xffffffff,0是一个完全有效的地址
pm100

8

但是由于内存寻址从0开始,难道不是0像其他地址一样有效吗?

在某些/许多/所有操作系统上,内存地址0在某种程度上是特殊的。例如,通常将其映射到无效/不存在的内存,如果尝试访问它,则会导致异常。

为什么负数不为null?

我认为指针值通常被视为无符号数字:否则,例如32位指针将只能寻址2 GB的内存,而不是4 GB。


4
我在设备上编码,其中地址零是有效地址,并且没有内存保护。空指针也是全零。如果您不小心写入了一个空指针,那么您将浏览位于零地址的OS设置;通常不会发生欢闹。
2014年

1
是:例如,在非保护模式的x86 CPU上,地址0是中断向量表
ChrisW

@ChrisW:在非保护模式x86上,地址零尤其是被零除中断向量,某些程序可能有完全正当的理由编写该地址。
超级猫

即使在可用存储将从物理地址零开始的平台上,C实现也可以轻松地使用地址零来保存从未使用过地址的对象,或者只是不使用内存的第一个字。在大多数平台上,“零比较”保存一条指令而不是“任何其他比较”,因此,即使浪费了第一个存储字也比使用非零地址来存储空值便宜。请注意,没有要求C标准未涵盖的事物(例如I / O端口或中断向量)的地址不等于null,也不要求...
supercat

...系统进程的空指针访问彼此之间有任何不同,因此,即使在对物理位置零的访问将是有用且有意义的系统上,全零也是“ null”的理想地址。
超级猫

5

我的猜测是选择了魔术值0来定义无效的指针,因为可以用更少的指令来测试它。某些机器语言在加载寄存器时会根据数据自动设置零和符号标志,因此您可以通过简单的加载然后测试分支指令来测试空指针,而无需执行单独的比较指令。

(不过,大多数ISA仅在ALU指令上设置标志,而不是在其上设置标志。通常,您不会通过计算生成指针,除非在解析C 源代码时在编译器中。但是至少您不需要任意的指针宽度常量来比较。)

在我最初使用的Commodore Pet,Vic20和C64机器上,RAM从位置0开始,因此如果您确实愿意,使用空指针进行读取和写入完全有效。


3

我认为这只是一个约定。必须有一些值来标记无效的指针。

您只会丢失一个字节的地址空间,这很少会成为问题。

没有负面的指针。指针始终是无符号的。同样,如果它们可能是负数,那么您的约定将意味着您丢失一半的地址空间。


注意:您实际上并没有丢失地址空间;您可以通过执行以下操作获取指向地址0的指针char *p = (char *)1; --p;。由于对空指针的行为标准定义的,这个系统能够具有p实际读写地址0,增量给的地址1,等等
MM

@MattMcNabb:一种实现方式,其中零地址是有效的硬件地址可能完全合法定义的行为char x = ((char*)0);来读取地址零和商店价值为X。此类代码将在任何未定义其行为的实现上产生未定义行为,但是标准说明某事为未定义行为的事实绝不禁止实现者提供自己的行为规范。
超级猫

@supercat ITYM *(char *)0。是的,但是在我的建议下,实现无需定义*(char *)0任何其他空指针操作的行为。
MM

1
@MattMcNabb:char *p = (char*)1; --p;只有在将指向对象的第一个字节以外的对象的指针强制转换为后,再执行该序列intptr_t,并且该转换的结果恰好产生值1时,才会由标准定义行为,在这种特殊情况下,的结果--p将产生一个指向该字节之前的字节的指针,该字节的指针值在转换为时intptr_t已产生1
超级猫

3

尽管C使用0表示空指针,但请记住,指针本身的值不能为零。但是,大多数程序员只会使用空指针实际上为0的系统。

但是为什么要零呢?嗯,这是每个系统共享的一个地址。通常情况下,低地址是为操作系统保留的,因此该值可以很好地用作应用程序的限制。偶然将整数值赋给指针的可能性和其他任何事物一样最终为零。


3
所有这些背后的更可能的原因是:分发预先初始化为零的内存很便宜,并且使该内存中的值表示有意义的值(如整数0,浮点0.0和空指针)很方便。C中初始化为零/空的静态数据不必占用可执行文件中的任何空间,并且在加载时会映射到填充零的块。零也可以在机器语言中得到特殊处理:轻松进行零比较,例如“如果等于零则分支”,等等。MIPS甚至具有一个虚拟寄存器,该寄存器只是一个零常数。
卡兹(Kaz)2012年

2

从历史上看,应用程序的内存不足被系统资源占用。正是在那几天,零成为默认的空值。

尽管这对于现代系统而言不一定是正确的,但将指针值设置为除分配给您的内存之外的任何值仍然不是一个好主意。


2

关于删除指针后不将其设置为null以便将来删除“暴露错误”的说法...

如果您真的对此感到担心,那么可以保证使用更好的方法(可以保证有效)是使用assert():


...
assert(ptr && "You're deleting this pointer twice, look for a bug?");
delete ptr;
ptr = 0;
...

这需要一些额外的输入,并在调试过程中进行额外的检查,但是可以肯定地为您提供了所需的信息:注意ptr被删除时“两次”。注释讨论中给出的替代方案(未将指针设置为null,否则会导致崩溃)根本无法保证会成功。更糟糕的是,与上述不同,如果这些“错误”之一进入货架,则可能导致用户崩溃(甚至更糟!)。最后,此版本使您可以继续运行程序以查看实际发生的情况。

我意识到这不能回答所提出的问题,但是我担心阅读评论的人可能会得出这样的结论:如果有可能将其发送给free()或删除两次。在少数情况下,将“未定义行为”用作调试工具永远不是一个好习惯。从来没有人寻找过最终由于删除无效指针而导致的错误的提议。这些错误需要花费数小时来追查,并且几乎总是以一种完全出乎意料的方式影响程序,这很难甚至不可能追溯到最初的问题。


2

许多操作系统使用全零位表示空指针的一个重要原因是,这意味着memset(struct_with_pointers, 0, sizeof struct_with_pointers)类似的操作会将内部的所有指针都设置struct_with_pointers为空指针。C标准不能保证这一点,但是很多程序都采用它。


1

在一台旧的DEC机器(我认为是PDP-8)中,C运行时将对内存的第一页进行内存保护,以便尝试访问该块中的内存将引发异常。


PDP-8没有C编译器。PDP-11没有内存保护,而VAX则以无声地将0返回给NULL指针取消引用而臭名昭著。我不确定这是指哪台机器。
fuz

1

前哨值的选择是任意的,而事实上,下一版本的C ++(正式称为“ C ++ 0x”,将来很有可能称为ISO C ++ 2011)解决了这一问题,并引入了关键字nullptr,表示空值指针。在C ++中,值0可用作任何POD和具有默认构造函数的任何对象的初始化表达式,并且在指针初始化的情况下,它具有分配哨兵值的特殊含义。至于为什么不选择负值,地址范围通常为0到2 N对于某些值N为-1。换句话说,地址通常被视为无符号值。如果将最大值用作前哨值,则必须根据内存大小在系统之间进行更改,而0始终是可表示的地址。它也出于历史原因而使用,因为内存地址0通常在程序中不可用,并且当今大多数操作系统都将部分内核加载到内存的较低页中,并且此类页通常采用以下方式进行保护:被程序(保存内核)触摸(取消引用)会导致错误。


1

它必须具有一定的价值。显然,您不想踩踏用户可能合法使用的值。我推测,由于C运行时为零初始化的数据提供了BSS段,因此将零解释为未初始化的指针值在某种意义上是有意义的。


0

很少有OS允许您写入地址0。即IDT,页表等。(这些表必须位于RAM中,将它们粘贴在底部比尝试确定RAM顶部在哪里更容易。)而且,没有合适的操作系统可以让您编辑系统表willy-nilly。

当K&R制作C时,这可能并不在意,但它(以及0 == null很容易记住的事实)使0成为流行的选择。


在保护模式下情况并非如此,实际上,在某些Linux配置上,您可以写入虚拟地址
0。– L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳ 2010年

0

该值0是一个特殊值,在特定表达式中具有各种含义。对于指针,正如已经多次指出的那样,之所以使用它,可能是因为当时它是“在此插入默认前哨值”的最方便的方法。作为常量表达式,在指针表达式的上下文中,它与按位零(即,所有位设置为零)的含义不同。在C ++中,有几种类型不具有按位零表示形式,NULL例如指针成员和指向成员函数的指针。

幸运的是,C ++ 0x有一个新的关键字用于“表达式,它表示一个已知的无效指针,该指针也不会映射为整数表达式的按位零” nullptr。尽管有一些可以使用C ++定位的系统,这些地址允许在不进行倒推的情况下取消对地址0的引用,所以请程序员当心。


0

在这个线程中已经有很多好的答案。首选0空指针的值可能有许多不同的原因,但我将再添加两个:

  • 在C ++中,零初始化指针会将其设置为null。
  • 在许多处理器上,将值设置为0或测试其等于/不等于0的效率要高于其他任何常数。

0

这取决于C / C ++中指针的实现。没有特殊的原因为什么在指针分配中NULL等效。


-1

有历史原因,但也有优化原因。

OS通常为进程提供一个初始化为0的内存页。如果程序想将该内存页的一部分解释为指针,则它为0,因此程序很容易确定该指针为未初始化。(将其应用于未初始化的Flash页面时,效果不佳)

另一个原因是,在许多处理器上,非常容易地测试一个值的等效性为0。有时,这是一个自由比较,不需要任何额外的指令,通常可以在另一个寄存器或第二个寄存器中不提供零值的情况下完成。作为指令流中要比较的文字。

大多数处理器的廉价比较是带符号的小于0且等于0。(这两个都暗示带符号的大于0且不等于0)

由于需要将所有可能值中的1个值保留为坏值或未初始化,因此您最好使它成为对坏值的等效性测试最便宜的值。对于以'\ 0'结尾的字符串也是如此。

如果要为此目的使用大于或小于0的地址,则最终会将地址范围切成两半。


-2

常量0是用来代替NULL,因为C是由前几年一些山顶洞人万亿制成NULLNILZIP,或NADDA将具有比全部采用更有意义0

但是由于内存寻址从0开始,难道不是0像其他地址一样有效吗?

确实。尽管许多操作系统都不允许您在地址0处映射任何内容,即使在虚拟地址空间中也是如此(人们意识到C是一种不安全的语言,并反映出空指针取消引用错误非常普遍,因此决定通过禁止它们来“修复”它们)映射到页面0的用户空间代码;因此,如果您调用一个回调但该回调指针为NULL,则最终将不会执行任何任意代码。

在这种情况下,如何使用0来处理空指针?

因为0与一个指针相比,将被某个实现特定的值替换,这是在malloc失败时malloc的返回值。

为什么负数不为null?

这将更加令人困惑。


您对“穴居人”等的观点可能是其根源,尽管我认为具体情况有所不同。演变成C的最早形式设计为在一种特定的体系结构上运行,其中an int不仅与指针大小相同-在许多情况下,an int和指针可以互换使用。如果一个例程期望一个指针,并且传入一个整数57,则该例程将使用与数字57具有相同位模式的地址。在那些特定的机器上,表示空指针的位模式为0,因此传递一个int 0将传递一个空指针。
超级猫

从那时起,C不断发展壮大,因此它可以用来为具有不同数字和指针表示形式的其他各种机器编写程序。虽然很少使用非零数字常量作为指针,但广泛使用常数零表示空指针。禁止这种用法会破坏现有代码,因此,期望编译器将数字零转换为实现用来表示空指针的任何形式。
超级猫

-4

阅读文章之前,请先阅读本段。我要求对阅读这篇文章感兴趣的任何人都应尝试仔细阅读它,当然,除非您完全理解它,否则请不要对它投反对票,谢谢。

现在是社区Wiki,因此,如果有人不同意任何概念,请对其进行修改,并清楚,详细地说明错误之处和原因,并尽可能引用源或提供可复制的证据。

回答

还有其他一些原因可能是NULL == 0的潜在因素

  1. 零是错误的事实,因此可以直接if(!my_ptr)代替if(my_ptr==NULL)
  2. 未初始化的全局整数默认情况下初始化为全零的事实,因此全零的指针将被视为未初始化。

在这里我想谈谈其他答案

不是因为语法糖

说NULL是因为语法糖,所以没有太大意义,如果是这样,为什么不使用数组的索引0来保持其长度呢?

实际上,C是与内部实现最相似的语言,说C仅仅因为语法糖就选择了零,是否有意义?他们宁愿提供关键字null(就像许多其他语言一样),而不是将零映射到NULL!

因此,到目前为止,它可能只是语法糖,很明显,C语言开发人员的初衷不是语法糖,正如我将进一步说明的那样。

1)规格

然而,虽然C规范确实从常量0说起了空指针(第6.3.2.3节),并且还定义了要实现的NULL定义(C11规范中的7.19和C99规范中的7.17),实际上,在5.4的发明人所写的“ C编程语言”一书中,有以下内容:

C保证零永远不会是数据的有效地址,因此零返回值可用于表示异常事件,在这种情况下,没有空间。

指针和整数不可互换,零是唯一的例外:常量零可以分配给指针,并且指针可以与常量零进行比较。符号常量NULL通常代替零,用作助记符,以更清楚地指示这是指针的特殊值。NULL在中定义。此后我们将使用NULL。

正如人们所看到的那样(从“零地址”开始),至少C语言作者的初衷是地址零,而不是常数零,而且从此摘录中可以看出,规范之所以从常量零可能不排除计算结果为零的表达式,而是包括整数常量零,这是唯一允许在不强制转换的情况下用于指针上下文的整数常量。

2)总结

尽管规范中并未明确指出零地址可以与零常量区别对待,但并未说明不能,并且在处理空指针常量时并未声明将其定义为实现。由NULL定义的常量进行运算,而不是声称它为零,这表明零常量和零地址之间可能存在差异。

(但是如果是这种情况,我只是想知道为什么要定义实现NULL,因为在这种情况下NULL也可以是常量零,因为编译器必须将所有零常量转换为实际的实现定义的NULL?)

但是,我在实际操作中看不到这一点,在通用平台中,地址零和常数零被视为相同,并抛出相同的错误消息。

此外,事实是,当今的操作系统实际上正在保留整个第一页(范围从0x0000到0xFFFF),只是为了防止由于C的NULL指针而访问零地址(请参见http://en.wikipedia.org/wiki/ Zero_page以及“ Jeffrey Richter和Christophe Nasarre的Windows Via C / C ++ Windows(由Microsoft Press出版)”。

因此,我会要求任何声称实际上已将其付诸实践的人要求指定平台和编译器以及他实际执行的确切代码(尽管由于规范中的定义含糊不清[正如我所展示的]任何编译器并且平台可以自由地做他想做的事情)。

但是,显然C的作者没有想到这一点,他们说的是“零地址”,并且“ C保证它永远不是有效地址”,以及“ NULL只是一个助记符”,清楚地表明它的初衷不是“语法糖”。

不是因为操作系统

还声称由于某些原因,操作系统拒绝访问地址零:

1)编写C时没有这种限制,就像在这个Wikipage上可以看到的那样:http://en.wikipedia.org/wiki/Zero_page

2)事实是C编译器确实访问了内存地址零。

这是BellLabs(http://www.cs.bell-labs.com/who/dmr/primevalC.html)的以下论文中的事实

两种编译器在处理方式上的细节不同。在较早的版本中,通过命名函数来找到起点。在后面的代码中,开始简单地设为0。这表明第一个编译器是在我们拥有内存映射的计算机之前编写的,因此程序的起始位置不在位置0,而在第二个位置,我们有一个确实提供映射的PDP-11。

(事实上​​,截至今天(正如我从Wikipedia和Microsoft Press引用的参考文献中所述),限制对零地址的访问的原因是由于C的NULL指针!因此最后,结果却是相反的!)

3)请记住,C也用于编写操作系统,甚至C编译器!

实际上,C是为了用它编写UNIX操作系统而开发的,因此,似乎没有理由说他们应该限制自己的地址为零。

关于计算机如何(物理上)能够访问地址零的(硬件)说明

我还要在这里解释另一点,怎么可能完全引用地址零?

想一想,地址由处理器获取,然后作为电压在内存总线上发送,然后由内存系统用于获取实际地址,但是地址为零将意味着没有电压,那么存储系统的物理硬件如何访问地址零?

答案似乎是,地址零是默认地址,换句话说,当内存总线完全断开时,内存系统始终可以访问地址零,因此任何不指定实际地址的读取或写入请求(地址为零的情况)自动访问地址零。


1
我没有投票给您,但是您的帖子有一些事实上的错误,例如 偏移量0的物理内存无法访问(由于所有开关都关闭了?是真的吗?),0和常数0可以互换(它们可能不是),其他则不能访问。
Hasturkun

关于0和常数0,这就是原始书所说的,而这是实际测试表明的,您发现两者之间真正的不同吗?如果是,哪个编译器和平台?尽管有许多答案表明存在差异,但我没有发现差异,也没有参考资料表明差异。事实上,根据en.wikipedia.org/wiki/Zero_page,还有“ Jeff C. Richter和Christophe Nasarre的Windows Via C / C ++ Windows”(由Microsoft Press出版)整个第一页!在现代计算机中受到保护只是为了防止空值(实际上浪费了一个以上字节!)
yoel halb 2013年

当然,地址的位模式用于选择要读取的内容。通常是这样。无论如何,我不想和你争论,我只是指出为什么你可能会被否决。
Hasturkun

我不同意你的主张。我也对继续进行讨论不感兴趣。
Hasturkun

6
硬件声明是胡说八道。要读取地址零,请驱动!Chip选择低,!RAS高,!CAS低,!WE高以及所有地址线为低。总线关闭时,!CS为高。
MSalters
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.