NULL宏什么时候不是0?

发布于 2024-08-28 13:57:53 字数 499 浏览 13 评论 0原文

我依稀记得几年前读过这篇文章,但我在网上找不到任何参考资料。

你能给我一个 NULL 宏没有扩展到 0 的例子吗?

为清楚起见进行编辑:今天它扩展为 ((void *)0)(0)(0L)。然而,有些架构早已被遗忘,但事实并非如此,并且 NULL 扩展到不同的地址。就像

#ifdef UNIVAC
     #define NULL (0xffff)
#endif

我正在寻找这样一台机器的例子。

更新以解决问题

我并不是在当前标准的背景下提出这个问题,也不是为了让人们因为我的错误术语而感到不安。然而,我的假设得到了公认的答案的证实:

后来的模型使用了[废话],显然是为了对所有现存的写得不好的 C 代码做出错误的假设。

有关当前标准中空指针的讨论,请参阅

I vaguely remember reading about this a couple of years ago, but I can't find any reference on the net.

Can you give me an example where the NULL macro didn't expand to 0?

Edit for clarity: Today it expands to either ((void *)0), (0), or (0L). However, there were architectures long forgotten where this wasn't true, and NULL expanded to a different address. Something like

#ifdef UNIVAC
     #define NULL (0xffff)
#endif

I'm looking for an example of such a machine.

Update to address the issues:

I didn't mean this question in the context of current standards, or to upset people with my incorrect terminology. However, my assumptions were confirmed by the accepted answer:

Later models used [blah], evidently as a sop to all the extant poorly-written C code which made incorrect assumptions.

For a discussion about null pointers in the current standard, see this question.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

傾旎 2024-09-04 13:57:53

C FAQ 有一些具有非 0 NULL 表示的历史机器的示例。

来自C 常见问题解答列表问题 5.17

问:说真的,有任何实际机器真正使用过非零 null
指针,或者指向不同的指针的不同表示
类型?

A:Prime 50 系列使用段 07777,偏移量 0 为空
指针,至少对于 PL/I 来说是这样。后来的型号使用段 0、偏移 0
C 中的空指针,需要新的指令,例如 TCNP(Test
C 空指针),显然是为了[脚注]所有现有的
写得不好的 C 代码做出了错误的假设。年纪大了,
字寻址 Prime 机器也因需要更大的内存而臭名昭著。
字节指针(char *)优于字指针(int *)。

Data General 的 Eclipse MV 系列具有三种架构
支持的指针格式(字、字节和位指针),其中两种
由 C 编译器使用:char *void * 的字节指针,以及 word
其他一切的指针。期间由于历史原因
32 位 MV 系列从 16 位 Nova 系列的演变,字
指针和字节指针具有偏移量、间接寻址和环
保护位位于字中的不同位置。传递不匹配的
函数的指针格式导致保护故障。
最终,MV C编译器添加了许多兼容性选项来尝试
处理存在指针类型不匹配错误的代码。

一些 Honeywell-Bull 大型机使用位模式 06000
(内部)空指针。

CDC Cyber​​ 180 系列具有 48 位指针,由环组成,
段和偏移量。大多数用户(在环 11 中)的空指针为
0xB00000000000。在旧的 CDC 补码机器上很常见
使用全一位字作为各种数据的特殊标志,
包括无效地址。

旧的 HP 3000 系列使用不同的字节寻址方案
地址比字地址;就像上面的几台机器一样
因此,它对 char *void * 使用不同的表示形式
指针比其他指针。

符号 Lisp 机器,一种标记架构,甚至没有
传统的数字指针;它使用 对(基本上是
不存在的 句柄)作为 C 空指针。

根据使用的“内存模型”,8086 系列处理器(PC
兼容)可以使用16位数据指针和32位函数
指针,反之亦然。

某些 64 位 Cray 机器在 a 的低 48 位中表示 int *
单词; char * 另外使用一些高 16 位来指示
字内的字节地址。

The C FAQ has some examples of historical machines with non-0 NULL representations.

From The C FAQ List, question 5.17:

Q: Seriously, have any actual machines really used nonzero null
pointers, or different representations for pointers to different
types?

A: The Prime 50 series used segment 07777, offset 0 for the null
pointer, at least for PL/I. Later models used segment 0, offset 0 for
null pointers in C, necessitating new instructions such as TCNP (Test
C Null Pointer), evidently as a sop to [footnote] all the extant
poorly-written C code which made incorrect assumptions. Older,
word-addressed Prime machines were also notorious for requiring larger
byte pointers (char *'s) than word pointers (int *'s).

The Eclipse MV series from Data General has three architecturally
supported pointer formats (word, byte, and bit pointers), two of which
are used by C compilers: byte pointers for char * and void *, and word
pointers for everything else. For historical reasons during the
evolution of the 32-bit MV line from the 16-bit Nova line, word
pointers and byte pointers had the offset, indirection, and ring
protection bits in different places in the word. Passing a mismatched
pointer format to a function resulted in protection faults.
Eventually, the MV C compiler added many compatibility options to try
to deal with code that had pointer type mismatch errors.

Some Honeywell-Bull mainframes use the bit pattern 06000 for
(internal) null pointers.

The CDC Cyber 180 Series has 48-bit pointers consisting of a ring,
segment, and offset. Most users (in ring 11) have null pointers of
0xB00000000000. It was common on old CDC ones-complement machines to
use an all-one-bits word as a special flag for all kinds of data,
including invalid addresses.

The old HP 3000 series uses a different addressing scheme for byte
addresses than for word addresses; like several of the machines above
it therefore uses different representations for char * and void *
pointers than for other pointers.

The Symbolics Lisp Machine, a tagged architecture, does not even have
conventional numeric pointers; it uses the pair <NIL, 0> (basically a
nonexistent <object, offset> handle) as a C null pointer.

Depending on the "memory model" in use, 8086-family processors (PC
compatibles) may use 16-bit data pointers and 32-bit function
pointers, or vice versa.

Some 64-bit Cray machines represent int * in the lower 48 bits of a
word; char * additionally uses some of the upper 16 bits to indicate a
byte address within a word.

妞丶爷亲个 2024-09-04 13:57:53

很久以前,它被键入为 ((void*)0) 或其他某种特定于机器的方式,其中该机器不使用全零位模式。

某些平台(某些 CDC 或 Honeywell 机器)对 NULL 有不同的位模式(即不是全零),尽管 ISO/ANSI 在 C90 批准之前通过指定 0 修复了这一点。 code> 是源代码中正确的 NULL 指针,无论底层位模式如何。来自 C11 6.3.2.3 Pointers /4 (不过,如上所述,这个措辞可以一直追溯到 C90):

值为 0 的整型常量表达式,或转换为 void * 类型的表达式,称为空指针常量。

There was a time long ago when it was typed as ((void*)0) or some other machine-specific manner, where that machine didn't use the all-zero bit pattern.

Some platforms (certain CDC or Honeywell machines) had a different bit pattern for NULL (ie, not all zeros) although ISO/ANSI fixed that before C90 was ratified, by specifying that 0 was the correct NULL pointer in the source code, regardless of the underlying bit pattern. From C11 6.3.2.3 Pointers /4 (though, as mentioned, this wording goes all the way back to C90):

An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant.

攒一口袋星星 2024-09-04 13:57:53

在 C 编译器中,它可以扩展为“((void *)0)”(但不必这样做)。这不适用于 C++ 编译器。

另请参阅 C 常见问题解答,其中有一整章介绍 空指针

In C compilers, it can expand to '((void *)0)' (but does not have to do so). This does not work for C++ compilers.

See also the C FAQ which has a whole chapter on null pointers.

对岸观火 2024-09-04 13:57:53

在 GNU libio.h 文件中:

#ifndef NULL
# if defined __GNUG__ && \
(__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 8))
#  define NULL (__null)
# else
#  if !defined(__cplusplus)
#   define NULL ((void*)0)
#  else
#   define NULL (0)
#  endif
# endif
#endif

注意 __cplusplus 上的条件编译。 C++ 不能使用 ((void*) 0),因为它对指针转换有更严格的规则;标准要求 NULL 为 0。C 允许 NULL 的其他定义。

In the GNU libio.h file:

#ifndef NULL
# if defined __GNUG__ && \
(__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 8))
#  define NULL (__null)
# else
#  if !defined(__cplusplus)
#   define NULL ((void*)0)
#  else
#   define NULL (0)
#  endif
# endif
#endif

Note the conditional compilation on __cplusplus. C++ can't use ((void*) 0) because of its stricter rules about pointer casting; the standard requires NULL to be 0. C allows other definitions of NULL.

千寻… 2024-09-04 13:57:53

C 编译器通常使用((void *)0)。原因是将 NULL 传递给具有可变参数的函数(或者现在很少见但仍然合法的没有原型的函数)。当指针大于 int 时,0 只会提升为 int,因此无法正确读取为指针。

C++ 编译器无法使用该定义,因为 C++ 不允许从 void * 进行隐式转换(将 0 转换为任何指针都是特殊情况)。然而,C++11 引入了新关键字 nullptr,它是特殊 nullptr_t 类型的空指针常量,可隐式转换为任何指针类型,但不能转换为数字。这解决了可变参数问题和隐式强制转换,以及重载选择方面更严重的问题(0 出于明显的原因选择 int 重载而不是指针 1)。为较旧的编译器自行定义这些是合法的,并且一些 C++ 编译器过去尝试过这样做。

C compilers usually use ((void *)0). The reason is passing NULL to functions with variable arguments (or now rare but still legal functions without prototype). When pointers are larger than int, 0 will only be promoted to int and will thus not read correctly as pointer.

C++ compilers can't use that definition because C++ does not permit implicit cast from void * (casting 0 to any pointer is special-cased). However C++11 introduced new keyword nullptr that is a null pointer constant of special nullptr_t type implicitly convertible to any pointer type, but not number. This solves both the variadic argument problem and the implicit cast and additionally more severe problems with overload selection (0 for obvious reason selects int overload over pointer one). It is legal to define these yourself for older compilers and some C++ compilers tried that in the past.

陌生 2024-09-04 13:57:53

在现代 C 语言中,void *pointer = 0; 旨在将“指针”初始化为不指向任何东西。是否通过将“指针”位设置为全零来实现这一点是特定于平台的。

过去,指针上下文中“0”的这种正式含义尚未建立。有必要将指针设置为平台视为“不指向任何地方”的实际值。例如,平台可能会选择一些永远不会将页面映射到它的固定地址。在这种情况下,在旧的编译器中,平台可能将 NULL 定义为:

#define NULL ((void*)0xFFFFF000)

当然,今天,没有理由不将其定义为 ((void*)0)

In modern C, void *pointer = 0; is meant to initialize "pointer" to not point at anything. It is platform-specific as to whether that is accomplished by setting the bits of "pointer" to all-zero.

In the past, this formal meaning of "0" in a pointer context was not established. It was necessary to set the pointer to the actual value that the platform treated as "doesn't point anywhere". As an example, a platform might choose some fixed address that never gets a page mapped to it. In this case, in an old compiler, the platform might have defined NULL as:

#define NULL ((void*)0xFFFFF000)

Of course, today, there's no reason not to define it as ((void*)0).

潜移默化 2024-09-04 13:57:53

C 中的 NULL 宏扩展为实现定义的空指针常量。它可以是任何东西(因为它是实现定义的),但在指针上下文中,效果始终与扩展为常量 0 相同。

在标准 C 历史中,从未有过 NULL 扩展到特定非 0 的情况,除非您将 (void *) 0 视为“不是 0”。但代表 NULL(void *) 0 至今仍被广泛使用。

NULL macro in C expands to implementation defined null-pointer constant. It can be anything (since it is implementation-defined), but in pointer context the effect is always the same as if it expanded to constant 0.

There has never been a time in standard C history when NULL expanded to something specifically not 0, unless you consider (void *) 0 as "not 0". But (void *) 0 for NULL is widely used to this day.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文