为什么 NULL/0 是对象的非法内存位置?

发布于 2024-09-03 13:14:04 字数 172 浏览 1 评论 0原文

我了解 C/C++ 中 NULL 常量的用途,并且我了解它需要在内部以某种方式表示。

我的问题是:是否有一些根本原因导致 0 地址对于 C/C++ 中的对象来说是无效的内存位置?或者我们理论上是否由于这一保留而“浪费”了一个字节的内存?

I understand the purpose of the NULL constant in C/C++, and I understand that it needs to be represented some way internally.

My question is: Is there some fundamental reason why the 0-address would be an invalid memory-location for an object in C/C++? Or are we in theory "wasting" one byte of memory due to this reservation?

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

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

发布评论

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

评论(12

慕烟庭风 2024-09-10 13:14:04

空指针实际上不必为 0。在 C 规范中保证,当在指针上下文中给出常量 0 值时,编译器会将其视为 null,但是如果这样做,

char *foo = (void *)1;
--foo;
// do something with foo

您将访问 0-地址,不一定是空指针。在大多数情况下,实际情况确实如此,但这不是必需的,因此我们实际上不必浪费该字节。虽然,从更大的角度来看,如果它不是 0,它一定是某个东西,所以一个字节在某处被浪费了

编辑:由于注释中的混乱,编辑了 NULL 的使用。另外,这里的主要信息是“空指针!= 0,这里有一些 C/伪代码显示了我想要表达的观点。”请不要真正尝试编译它或担心类型是否正确;意思很清楚。

The null pointer does not actually have to be 0. It's guaranteed in the C spec that when a constant 0 value is given in the context of a pointer it is treated as null by the compiler, however if you do

char *foo = (void *)1;
--foo;
// do something with foo

You will access the 0-address, not necessarily the null pointer. In most cases this happens to actually be the case, but it's not necessary, so we don't really have to waste that byte. Although, in the larger picture, if it isn't 0, it has to be something, so a byte is being wasted somewhere

Edit: Edited out the use of NULL due to the confusion in the comments. Also, the main message here is "null pointer != 0, and here's some C/pseudo code that shows the point I'm trying to make." Please don't actually try to compile this or worry about whether the types are proper; the meaning is clear.

赤濁 2024-09-10 13:14:04

这与浪费内存无关,更多的是与内存组织有关。

当您使用内存空间时,您必须假设任何不直接“属于您”的东西都是由整个系统共享的或者您访问是非法的。如果您已获取堆栈上仍在堆栈上的某些内容的地址,或者您已从动态内存分配器接收到该地址但尚未回收它,则该地址“属于您”。一些操作系统调用还会为您提供合法区域。

在过去的实模式(例如DOS)的美好时光中,机器地址空间的所有开头根本不应该由用户程序写入。其中一些甚至映射到 I/O 之类的东西。
例如,写入 0xB800(相当低)的地址空间实际上可以让您捕获屏幕!地址 0 处从未放置过任何内容,并且许多内存控制器不允许您访问它,因此它是 NULL 的绝佳选择。事实上,如果您尝试在那里写入,某些 PC 上的内存控制器会变得疯狂。

如今,操作系统通过虚拟地址空间来保护您。然而,任何进程都不允许访问未分配给它的地址。大多数地址甚至没有映射到实际的内存页面,因此访问它们将触发一般保护错误或操作系统中的等效错误。这就是为什么 0 没有被浪费——即使你机器上的所有进程“都有一个地址 0”,如果它们尝试访问它,它也不会映射到任何地方。

This has nothing to do with wasting memory and more with memory organization.

When you work with the memory space, you have to assume that anything not directly "Belonging to you" is shared by the entire system or illegal for you to access. An address "belongs to you" if you have taken the address of something on the stack that is still on the stack, or if you have received it from a dynamic memory allocator and have not yet recycled it. Some OS calls will also provide you with legal areas.

In the good old days of real mode (e.g., DOS), all the beginning of the machine's address space was not meant to be written by user programs at all. Some of it even mapped to things like I/O.
For instance, writing to the address space at 0xB800 (fairly low) would actually let you capture the screen! Nothing was ever placed at address 0, and many memory controller would not let you access it, so it was a great choice for NULL. In fact, the memory controller on some PCs would have gone bonkers if you tried writing there.

Today the operating system protects you with a virtual address space. Nevertheless, no process is allowed to access addresses not allocated to it. Most of the addresses are not even mapped to an actual memory page, so accessing them will trigger a general protection fault or the equivalent in your operating system. This is why 0 is not wasted - even though all the processes on your machine "have an address 0", if they try to access it, it is not mapped anywhere.

一曲琵琶半遮面シ 2024-09-10 13:14:04

没有要求空指针等于 0 地址,只是大多数编译器都是这样实现的。通过存储一些其他值来实现空指针是完全可能的,事实上一些系统这样做。 C99 规范 §6.3.2.3(指针) 仅指定值为 0 的整型常量表达式是空指针常量,但并没有说明空指针转换为整数时的值为 0。

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

任何指针类型都可以转换为整数类型。除先前指定的情况外,
结果是实现定义的。如果结果不能用整数类型表示,
该行为是未定义的。结果不必在任何整数的值范围内
类型。

在某些嵌入式系统上,零内存地址用于可寻址的内容。

There is no requirement that a null pointer be equal to the 0-address, it's just that most compilers implement it this way. It is perfectly possible to implement a null pointer by storing some other value and in fact some systems do this. The C99 specification §6.3.2.3 (Pointers) specifies only that an integer constant expression with the value 0 is a null pointer constant, but it does not say that a null pointer when converted to an integer has value 0.

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

Any pointer type may be converted to an integer type. Except as previously specified, the
result is implementation-defined. If the result cannot be represented in the integer type,
the behavior is undefined. The result need not be in the range of values of any integer
type.

On some embedded systems the zero memory address is used for something addressable.

老子叫无熙 2024-09-10 13:14:04

零地址和 NULL 指针(不一定)不是同一件事。只有文字零才是空指针。换句话说:

char* p = 0; // p is a null pointer

char* q = 1;
q--; // q is NOT necessarily a null pointer

系统可以自由地以它们选择的任何方式在内部表示空指针,并且这种表示可能会也可能不会通过使实际的 0 地址非法来“浪费”一个字节的内存。但是,编译器需要将文字零指针转换为系统内部的 NULL 表示形式。通过某种方式(除了分配文字零)指向零地址的指针不一定为空。

现在,大多数系统确实使用 0 来表示 NULL,但并非必须如此。

The zero address and the NULL pointer are not (necessarily) the same thing. Only a literal zero is a null pointer. In other words:

char* p = 0; // p is a null pointer

char* q = 1;
q--; // q is NOT necessarily a null pointer

Systems are free to represent the null pointer internally in any way they choose, and this representation may or may not "waste" a byte of memory by making the actual 0 address illegal. However, a compiler is required to convert a literal zero pointer into whatever the system's internal representation of NULL is. A pointer that comes to point to the zero address by some way other than being assigned a literal zero is not necessarily null.

Now, most systems do use 0 for NULL, but they don't have to.

爱给你人给你 2024-09-10 13:14:04

它不一定是非法内存位置。我通过取消引用指向零的指针来存储数据...碰巧该数据是存储在位于地址零的向量中的中断向量。

按照惯例,应用程序代码通常不使用它,因为历史上许多系统的重要系统信息都是从零开始的。它可能是引导 ROM 或向量表,甚至是未使用的地址空间。

It is not necessarily an illegal memory location. I have stored data by dereferencing a pointer to zero... it happens the datum was an interrupt vector being stored at the vector located at address zero.

By convention it is not normally used by application code since historically many systems had important system information starting at zero. It could be the boot rom or a vector table or even unused address space.

韵柒 2024-09-10 13:14:04

在许多处理器上,地址 0 是重置向量,其中包含 bootrom(PC 上的 BIOS),因此您不太可能在该物理地址存储任何内容。在具有 MMU 和支持操作系统的处理器上,物理地址和逻辑地址不需要相同,并且地址零可能不是执行进程上下文中的有效逻辑地址。

On many processors address zero is the reset vector, wherein lies the bootrom (BIOS on a PC), so you are unlikely to be storing anything at that physical address. On a processor with an MMU and a supporting OS, the physical and logical address addresses need not be the same, and the address zero may not be a valid logical address in the executing process context.

病毒体 2024-09-10 13:14:04

NULL 通常是零地址,但它是应用程序虚拟地址空间中的零地址。您在大多数现代操作系统中使用的虚拟地址与实际物理地址完全无关,操作系统为您从虚拟地址空间映射到物理地址。所以,不,使用代表 NULL 的虚拟地址 0 不会浪费任何内存。

如果您好奇的话,请阅读虚拟内存,以获得更深入的讨论。

NULL is typically the zero address, but it is the zero address in your applications virtual address space. The virtual addresses that you use in most modern operating systems have exactly nothing to do with actual physical addresses, the OS maps from the virtual address space to the physical addresses for you. So, no, having the virtual address 0 representing NULL does not waste any memory.

Read up on virtual memory for a more involved discussion if you're curious.

峩卟喜欢 2024-09-10 13:14:04

我没有看到直接解决我认为你所问问题的答案,所以这里是:

是的,由于使用的常量,至少有 1 个地址值被“浪费”(无法使用) 。它是否映射到进程内存的线性映射中的 0 并不相关。

地址不会用于数据存储的原因是您需要空指针的特殊状态,以便能够与任何其他实际指针区分开。就像 ASCIIZ 字符串(C 字符串,NUL 终止)的情况一样,其中 NUL 字符被指定为字符串结尾,不能在字符串内部使用。里面还能用吗?是的,但这会误导库函数关于字符串结束的位置。

我能想到至少一种我正在学习的 LISP 实现,其中 NIL(Lisp 的 null)不是 0,也不是无效地址而是真实对象。原因非常聪明 - 标准要求 CAR(NIL)=NIL 和 CDR(NIL)=NIL (注意:CAR(l) 返回指向列表的头/第一个元素的指针,其中 CDR(l) 返回 ptr列表的尾部/其余部分。)。因此,他们没有在 CAR 和 CDR 中添加 if-checks 指针是否为 NIL(这会减慢每次调用),而是分配一个 CONS(思考列表)并指定其头和尾指向自身。那里! - 这样 CAR 和 CDR 将起作用,并且内存中的地址将不会被重用(因为它被设计为 NIL 的对象占用)

ps。我只记得很多很多年前我读到了一些与 NULL 相关的 Lattice-C 错误 - 一定是在黑暗的 MS-DOS 分段时代,你使用单独的代码段和数据段 - 所以我记得存在一个问题,链接库中的第一个函数可能具有地址 0,因此指向它的指针将被视为无效,因为 ==NULL

I don't see the answers directly addressing what i think you were asking, so here goes:

Yes, at least 1 address value is "wasted" (made unavailable for use) because of the constant used for null. Whether it maps to 0 in linear map of process memory is not relevant.

And the reason that address won't be used for data storage is that you need that special status of the null pointer, to be able to distinguish from any other real pointer. Just like in the case of ASCIIZ strings (C-string, NUL-terminated), where the NUL character is designated as end of character string and cannot be used inside strings. Can you still use it inside? Yeah but that will mislead library functions as of where string ends.

I can think of at least one implementation of LISP i was learning, in which NIL (Lisp's null) was not 0, nor was it an invalid address but a real object. The reason was very clever - the standard required that CAR(NIL)=NIL and CDR(NIL)=NIL (Note: CAR(l) returns pointer to the head/first element of a list, where CDR(l) returns ptr to the tail/rest of the list.). So instead of adding if-checks in CAR and CDR whether the pointer is NIL - which will slow every call - they just allocated a CONS (think list) and assigned its head and tail to point to itself. There! - this way CAR and CDR will work and that address in memory won't be reused (because it is taken by the object devised as NIL)

ps. i just remembered that many-many years ago i read about some bug of Lattice-C that was related to NULL - must have been in the dark MS-DOS segmentation times, where you worked with separate code segment and data segment - so i remember there was an issue that it was possible for the first function from a linked library to have address 0, thus pointer to it will be considered invalid since ==NULL

像极了他 2024-09-10 13:14:04

但由于现代操作系统可以将物理内存映射到逻辑内存地址(或者更好:从 386 开始的现代 CPU),因此甚至没有浪费一个字节。

But since modern operating systems can map the physical memory to logical memory addresses (or better: modern CPUs starting with the 386), not even a single byte is wasted.

夏夜暖风 2024-09-10 13:14:04

正如人们已经指出的那样,NULL 指针的位表示不必与 0 值的位表示相同。尽管在几乎所有情况下(可以忽略具有特殊地址的旧恐龙计算机),因为 NULL 指针也可以用作布尔值,并且通过使用整数(足够大小)来保存指针值,更容易代表现代 CPU 的常见 ISA。处理它的代码更加直接,因此更不容易出错。

As people already have pointed out, the bit representation of the NULL pointer has not to be the same as the bit represention of a 0 value. It is though in nearly all cases (the old dinosaur computers that had special addresses can be neglected) because a NULL pointer can also be used as a boolean and by using an integer (of suffisent size) to hold the pointer value it is easier to represent in the common ISAs of modern CPU. The code to handle it is then much more straight forward, thus less error prone.

删除会话 2024-09-10 13:14:04

您正确地注意到 0 处的地址空间对于您的程序来说不可用。由于多种原因,各种系统无论如何都不认为这是程序的有效地址空间。

允许使用任何有效地址将需要所有指针的空值标志。这将超出地址 0 处丢失内存的开销。还需要额外的代码来检查该地址是否为空,从而浪费内存和处理器周期。

理想情况下,NULL 指针使用的地址(通常为 0)应该在访问时返回错误。 VAX/VMS 从未将页面映射到地址 0,因此跟随 NULL 指针将导致失败。

You are correct in noting that the address space at 0 is not usable storate for your program. For a number of reasons a variety of systems do not consider this a valid address space for your program anyway.

Allowing any valid address to be used would require a null value flag for all pointers. This would exceed the overhead of the lost memory at address 0. It would also require additional code to check and see if the address were null or not, wasting memory and processor cycles.

Ideally, the address that NULL pointer is using (usually 0) should return an error on access. VAX/VMS never mapped a page to address 0 so following the NULL pointer would result in a failure.

萌化 2024-09-10 13:14:04

该地址处的内存被保留供操作系统使用。 0 - 64k 被保留。 0 用作特殊值,向开发人员指示“不是有效地址”。

The memory at that address is reserved for use by the operating system. 0 - 64k is reserved. 0 is used as a special value to indicate to developers "not a valid address".

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