为什么NULL不是由编译器预定义的

时间:2013-04-20 19:39:12

标签: c

这个问题困扰了我一段时间。我从来没有看到过NULL的不同定义,它总是

#define NULL  ((void *) 0)

是否有任何体系结构,其中NULL的定义不同,如果是,为什么编译器不为我们声明这个?

4 个答案:

答案 0 :(得分:4)

WhozCraig将这些评论写入现已删除的answer,但可以将其提升为完整答案(这就是我在这里所做的)。他指出:

  

有趣的说明:AS / 400是一个非常独特的平台,任何非有效指针都被视为等同于NULL。他们用来做这件事的机制简直太棒了。在这种意义上,“有效”是任何128位指针(平台使用128位线性地址空间用于所有),其中包含由已知可信指令集获得的“值”。很难相信,int *p = (int *)1; if (p) { printf("foo"); }在该平台上打印“foo”。分配给p的值不是源信任的,因此被视为“无效”,因此等同于NULL

     

坦率地说它是如何运作的。进程的映射虚拟地址空间中的每个16字节段落在进程范围的位图中具有相应的“位”。所有指针必须位于其中一个段落边界上。如果该位是“亮起”,则相应的指针存储在受信任的源中,否则它是无效的并且等效于NULL。在确定该位是否点亮时,都会仔细检查对malloc,指针数学等的调用。正如你可以想象的那样,在结构中加入指针会给结构包装的想法带来全新的伤害。


这是标记的社区维基(这不是我的答案 - 我不应该得到信用)但如果WhozCraig写下自己的答案,它可以被删除。

这表明存在具有有趣指针属性的真实平台。

有些平台#define NULL ((void *)0)不是通常的定义;在某些平台上,只要编译器理解它,它就可以只是0,在其他平台上,0L0ULL或其他适当的值。 C ++不喜欢((void *)0)作为定义;标头与C ++交互工作的系统可能不会使用void指针版本。

我在一台机器上学习了C,其中给定内存位置的char *地址的表示与同一内存位置的int *地址不同。这是在void *之前的日子,但这意味着您必须正确声明malloc()char *malloc(); - 没有原型),并且您必须显式地将返回值强制转换为正确的类型或你有核心转储。感谢C标准(虽然有问题的机器,ICL Perq - 来自三河的硬件 - 在很大程度上取决于标准的定义时间。)

答案 1 :(得分:4)

C 2011标准,online draft

6.3.2.3指针
...
3一个整数常量表达式,其值为0,或者此类表达式强制转换为类型 void *,称为空指针常量 66)如果将空指针常量转换为a 指针类型,结果指针,称为空指针,保证比较不等于指向任何对象或函数的指针。
66)宏NULL<stddef.h>(和其他标题)中定义为空指针常量;见7.19。

NULL 始终定义为零值常量表达式;它可以是裸0,或0转换为void *,或其他一些评估为0的积分表达式。​​就您的源代码而言,NULL将永远评估为0.

一旦代码被翻译,任何出现的空指针常量(0,NULL等)都将被底层架构用于空指针的任何内容替换,空指针可能为0也可能不为0 -valued。

答案 2 :(得分:1)

在ANSI-C之前的黑暗时代,旧的K&amp; R C在硬件上有许多不同的实现,这些实现在今天被认为是奇怪的。这是在机器非常“真实”的VM时代之前。零地址不仅在这些机器上很好,零地址可能很受欢迎......我认为CDC有时将系统常量零归零(如果设置为非零则会发生奇怪的事情) )。

 if ( NULL != ptr )     /* like this */
 if ( ptr )             /* never like this */

诀窍是找到你可以安全地用来表示“没有”的地址,因为在内存末端存储东西也很流行,这在一些架构上排除了0xFFFF。这些架构倾向于使用字地址而不是字节地址。

答案 3 :(得分:0)

我不知道答案,但我在猜测。在C中,您通常会执行很多malloc,因此会对返回的指针进行多次测试。由于malloc在失败时返回void *,尤其是(void *)0,因此为了测试malloc成功,NULL是一个自然界定的东西。由于这非常重要,其他库函数也使用NULL(或(void *)0),就像fopen一样。实际上,所有返回指针的东西。

因此,没有理由在语言级别定义它 - 它只是一个特殊的指针值,可以由很多函数返回。