3

最近有人指出我的一个 C 程序,如果内存块的起始地址足够低,我的一个测试将由于绕零而失败,从而导致崩溃。

起初我以为“这是一个令人讨厌的潜在错误”,但后来,我想知道:这种情况会发生吗?我从来没有见过。公平地说,这个程序已经在无数系统上运行了数百万次,而且迄今为止从未发生过。

malloc()因此,我的问题是:调用可能返回的最低内存地址是多少?据我所知,我从未见过像 0x00000032 这样的地址。

我只对“现代”环境感兴趣,例如 Linux、BSD 和 Windows。此代码不打算在 C64 或任何爱好/研究操作系统上运行。

4

3 回答 3

8

首先,既然这就是你所要求的,我只会考虑现代系统。这意味着他们正在使用分页内存并且在 0 处有一个错误页面来处理空指针取消引用。

现在,我所知道的任何真实系统上的最小页面大小是 4k(4096 字节)。这意味着您永远不会有低于 0x1000 的有效地址;任何较低的内容都将是包含零地址的页面的一部分,因此将排除空指针取消引用错误。

在现实世界中,好的系统实际上可以防止你走得那么低。现代 Linux 甚至阻止应用程序故意将页面映射到低于可配置的默认值(我相信是 64k)。这个想法是,您甚至希望从空指针(例如p[n]p恰好是空指针的位置)到故障(在 Linux 的情况下,他们希望内核空间中的代码在尝试访问此类地址时出现故障以避免可能导致特权提升漏洞的内核空指针取消引用错误)。

话虽如此,在指针指向的数组边界之外执行指针算术是未定义的行为。即使地址没有换行,编译器也可能会做各种各样的事情(无论是为了强化你的代码,还是为了优化),其中未定义的行为可能会导致你的程序中断。好的代码应该遵循编写它的语言的规则,即不要调用未定义的行为,即使您希望 UB 是无害的。

于 2012-12-08T23:10:57.410 回答
4

您可能的意思是您正在计算&a - 1或类似的东西。

请不要这样做,即使指针比较当前在大多数体系结构上被实现为无符号比较,并且您知道这(uintptr_t)&a比当前系统上的某些任意界限大。编译器利用未定义的行为进行优化。他们现在就这样做,如果他们现在不利用它,他们将来会利用它,而不管您可能期望从指令集或平台获得的“保证”如何。

有关更多信息,请参阅这个广为人知的轶事

在完全不同的寄存器中,您可能会认为 C 语言中未定义有符号溢出,因为过去存在不同的硬件选择,例如 1 的补码和符号幅度。因此,如果您知道平台是 2 的补码,则表达式(x+1) > x会检测MAX_INT.

这可能是历史原因,但推理不再成立。现代编译器对表达式(x+1) > x(with xof type int) 进行了优化1,因为有符号溢出是 undefined。编译器作者并不关心未定义的最初原因是可用架构的多样性。你用指针做的任何未定义的事情都是他们列表中的下一个。如果您调用未定义的行为,您的程序明天就会中断,这不是因为架构发生了变化,而是因为编译器在优化方面越来越激进。

于 2012-12-08T23:10:47.913 回答
2

动态分配在 上执行heapHeap驻留在(程序代码)和部分address space之后的进程中,请参见此处:http ://www.cprogramming.com/tutorial/virtual_memory_and_heaps.html 。因此堆中可能的最小地址取决于这 3 个段的大小,因此没有绝对的答案,因为它取决于特定的程序。textinitialized datauninitialized data

于 2012-12-08T23:17:07.850 回答