Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/68.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 为什么NULL不是有效的内存地址?_C_Null - Fatal编程技术网

C 为什么NULL不是有效的内存地址?

C 为什么NULL不是有效的内存地址?,c,null,C,Null,这听起来可能是个愚蠢的问题,但因为在C中,NULL的字面定义是 #define NULL 0 为什么它不能是一个有效的内存地址?为什么我不能取消引用它,为什么任何数据都不可能位于内存地址0 我确信这个问题的答案是“前n个字节的内存总是由内核保留”之类的,但我在互联网上找不到类似的东西 我的另一个理由是,这不是平台独立的吗?难道我不能发明一种新的体系结构,让进程可以访问内存地址0吗?取消引用NULL就是这样。任何事情都有可能发生,大多数时候坏事都会发生。就这样吧 一些旧的体系结构(…)允许您取消

这听起来可能是个愚蠢的问题,但因为在C中,
NULL
的字面定义是

#define NULL 0
为什么它不能是一个有效的内存地址?为什么我不能取消引用它,为什么任何数据都不可能位于内存地址0

我确信这个问题的答案是“前n个字节的内存总是由内核保留”之类的,但我在互联网上找不到类似的东西


我的另一个理由是,这不是平台独立的吗?难道我不能发明一种新的体系结构,让进程可以访问内存地址0吗?

取消引用
NULL
就是这样。任何事情都有可能发生,大多数时候坏事都会发生。就这样吧

一些旧的体系结构(…)允许您取消限制
NULL

C11标准规范(read)不要求
NULL
指针全部为零位(请参阅);它可以是其他的东西,但它应该是一个永远无效的地址,因此不能通过成功的
malloc
或操作员地址(一元
&
)获得,从C11的意义上说。但是这样做更方便,实际上大多数(但不是所有)C实现都这样做

IIRC,在Linux上,您可以使用
MAP\u FIXED
来创建包含
(void*)0
的页面,但这样做是不明智的(例如,因为允许一致的优化编译器优化
NULL
的解引用)

因此,
(void*)0
实际上不是一个有效的地址(在一些MMU和虚拟内存运行足够好的操作系统的普通处理器上!),因为可以方便地确定它是
NULL
,并且可以方便地确保它给出一个有效的地址。但这不是C标准所要求的(在今天廉价的微控制器上也是错误的)

C实现必须提供某种方式来表示
NULL
指针(并保证它永远不是某个有效位置的地址)。这甚至可以通过约定来实现:例如,提供完整的232字节地址空间,但承诺永远不使用地址0(或为
NULL
指定的任何地址,可能是42!)

NULL
恰好是可解除的时,细分错误不会捕获到细微的文件错误(因此C程序更难调试)

我不能发明一种新的体系结构,让进程可以访问内存地址0吗

您可以,但您不想这样做(如果您关心提供任何符合标准的C实现)。您希望将地址0设置为
NULL
。否则,编写C编译器(和标准C库)将更加困难。并使该地址无效,甚至出现分段错误,从而使调试(以及用户使用C编写代码的生活)更容易


如果你梦见奇怪的架构,请阅读Liam Proven在FOSDEM2018上的演讲(和、和)。这真的很有启发性,也是一次很好的谈话。

使地址为零未映射,以便在程序试图访问它时出现陷阱,这是许多操作系统提供的一种便利。这不是C标准所要求的

根据C标准:

  • NULL
    不是任何对象或函数的地址。(具体地说,它要求
    NULL
    compare不等于任何对象或函数的指针。)
  • 如果将
    *
    应用于
    NULL
    ,则标准不会定义结果行为
这意味着您可以使用
NULL
作为指针未指向任何对象或函数的指示器。这是C标准为
NULL
-提供的唯一用途,以使用is测试,例如
if(p!=NULL).
。当
p
NULL
时,如果使用
*p
,C标准不保证会出现陷阱

换句话说,C标准不要求
NULL
提供任何捕获能力。它只是一个不同于任何实际指针的值,只要您有一个表示“不指向任何东西”的指针值

通用操作系统通常会安排地址为零的内存被取消映射(它们的C实现将
NULL
定义为
(void*)0
或类似的内容),这样,如果取消引用空指针,就会出现陷阱。当他们这样做时,他们被扩展到C语言,超出了规范的要求。它们故意将地址0从进程的内存映射中排除,以使这些陷阱工作

然而,C标准并不要求这样做。C实现可以自由地将内存映射到地址零处,并且,当您将
*
应用到空指针时,其中可能有数据,并且如果操作系统允许,您的程序可以读取和/或写入该数据。完成此操作后,通常在操作系统内核(如设备驱动程序、内核扩展或内核本身)或嵌入式系统或其他具有简单操作系统的专用系统内运行的代码中。空指针常量(
null
)为0值。空指针值可能不是0。在转换过程中,编译器将用实际的空指针值替换出现的空指针常量


NULL
不代表“地址0”;相反,它表示一个定义良好的无效指针值,保证不指向任何对象或函数,并且尝试取消引用无效指针会导致未定义的行为。

NULL的定义是实现定义的:@MartinR我的问题与此不同。我问的是为什么0不是一个有效的内存地址,我不是问取消引用NULL I