当我将它添加到指针时,我可以安全地忽略溢出检查的最小偏移量是多少?

Posted

技术标签:

【中文标题】当我将它添加到指针时,我可以安全地忽略溢出检查的最小偏移量是多少?【英文标题】:What is smallest offset for which I can safely omit overflow checking when I add it to a pointer? 【发布时间】:2009-04-05 09:35:15 【问题描述】:

我是否可以期望用户空间程序中的任何“数据”指针与地址 0 和 0xffffffff... 保持安全距离,这样我就可以安全地为指针添加一个小偏移量而不检查溢出?当 p 是指向常量字符缓冲区或动态分配字符串的 char 指针(在现代 >= 32 位操作系统上)时,我可以安全地假设 p + n 不会溢出的最大正 n 是多少?

为了避免混淆:我说的是溢出检查,而不是边界检查。例如:如果您有一个指针 p 指向具有 m 个字符的字符串的开头,并且您想在正偏移量 i 处访问该字符,那么您需要检查 i = p.

更新:好的,如果 i > m,p + i 不是有效的标准 C,无论 p + i 是否实际取消引用或是否溢出。但是,我真正感兴趣的问题是,是否存在一个小的 n,p + n 在实践中不会溢出 。回答这个问题显然需要一些关于现代操作系统如何组织地址空间的知识。

Update2:听到任何一个特定的平台已经很有趣了,即使它不是通用的。最好不是一些晦涩难懂的嵌入式。 x86 或基于 Power 的 32 位 Win、Linux 和 Mac 将是最有趣的。

【问题讨论】:

不要这样做!唯一安全的 n 是 0。 即使冒险进入特定于平台(从 C++ 的角度来看,未定义)的行为,也没有单一的答案。并非每个“现代 >=32 位操作系统”都将数据放在相同的地址范围内。您的问题没有意义,这表明您只是以错误的方式解决这个问题。 不,但我假设大多数/所有人都不会将数据直接放在地址限制(0 和 0xffffffff)旁边。或者您是否了解任何一个数据接近极限的特定平台? 【参考方案1】:

您可以安全地添加到指针(然后取消引用它)的唯一偏移量是将指针定位在您正在使用的内存块中的偏移量。此块必须已使用 new 或 malloc 分配,或者存在于堆栈中。

无论哪种方式,都可以保证内存存在(并且超过内存末尾的地址是一个合理的地址),否则你会得到一个来自 new 的异常,一个来自 malloc 的 NULL 指针或未定义的行为,如果你尝试在堆栈上错误分配。在任何情况下,您都不必检查溢出。

【讨论】:

请查看更新后的问题。我不是在谈论边界检查,而是在谈论溢出检查。是的,有时你必须检查溢出。 您似乎在寻找一种检查任何指针(因为 p + i 可以具有任何值)是否有效的方法 - 没有办法做到这一点。但是,这不是正确编写代码的问题, 不,我不是。我正在尝试优化性能关键库函数中的边界检查代码。 这并不能解释为什么你不只使用 i @Stephan:在问题中需要这个......并且意味着 i 【参考方案2】:

严格来说答案是 0,因为 p 可能已经指向数组末尾之后的一个,这就是标准所说的有效。

在特定的实现中,您可能能够摆脱一些限制,但这是完全由实现定义的。已经有硬件检查 CPU 指令中指针的操作,也许现在仍然存在:如果 p 指向 2 个整数的数组,则执行 p+3 将导致 CPU 执行指令失败。另一方面,在大多数当前的硬件上,你可以摆脱很多。

【讨论】:

【参考方案3】:

根据您提供的信息,答案是 0。 0 是根据 C++ 标准唯一有效的答案。

如果您愿意冒险从事未定义的行为(提示:不要),您必须向我们提供一些特定于平台的信息任何挥手告别> 保证您的应用程序状态的有效性。您的应用程序可能仍在运行,但您依赖于操作系统和编译器编写者做出的任意且可能不断变化的决策。

如果我们知道您的平台的确切细节(主要是哪个 CPU、操作系统、哪个编译器),那么可能会给您一个通常可以工作的答案,只要什么都没有编译器或操作系统的变化。

但您似乎走错了路。如果这对性能至关重要,就像您一直说的那样,安排它,以免指针溢出成为问题

严格来说,将 anything 添加到指针以使其指向之前指向的同一块内存是未定义的。它可能会起作用,或者它可能在某些架构上表现得非常时髦。它可能会在意想不到的时候溢出,它可能会导致一些硬崩溃。这就是为什么该语言只是说“不允许”,以及为什么我们无法说出在您没有告诉我们它运行的平台时实际会发生什么。

C++ 指针不是内存地址。这就是编译器通常实现它的方式,是的,但它们遵循不同的规则。根据 CPU 指令集,有些事情对于内存地址是合法的,而对于指针是不合法的。

但说真的,我最好的建议是:退后一步,检查如何避免需要检查溢出。

【讨论】:

【参考方案4】:

在 C 中,指针算术和相对比较仅在“对象”中定义(不要与 C++ 对象混淆)。 “对象”是一个变量(任何类型)或一个用 malloc/calloc/realloc 分配的内存区域。您可以计算一个指向“过去”对象的指针,这将始终在符合标准的实现中工作。

从较低级别来看,指针通常实现为(无符号)整数。整数的大小足以容纳任何内存位置的地址。指针溢出的唯一方法是超出地址空间,而这在符合 C 标准的情况下是不可能的。

但是,如果您正在编写低级代码,并忽略指针运算仅在对象内有效的限制,那么最好的方法是利用指针如何表示的知识。在大多数现代环境中,这与使用无符号整数算术检查溢出是一样的。

(例外情况是分段内存架构、8086 或 Multics 类型,以及我可能从记忆中抑制到的其他内容 保持我的理智。)

【讨论】:

【参考方案5】:

在我看来,这似乎非常依赖:

操作系统 底层硬件 编译器

作为一项规则,仅据我所知,堆栈分配在线性地址空间的顶部。考虑到您正在运行各种运行时库,您的实际数据很可能不在该空间的顶部运行。不管怎样,如果你超出了malloc分配的区域,你会遇到其他麻烦,如果你超出了你的堆栈帧,你也会遇到麻烦。最重要的是,我认为您不必担心从 0xffffffffffffffff 到 0x0 的环绕,但您仍然必须确保不会超出静态、自动或手动分配的内存的界限。

【讨论】:

【参考方案6】:

这取决于两件事

架构 编译器

因此,唯一确定的方法是查阅编译器和架构的参考文档。

【讨论】:

【参考方案7】:

通常内存分配应该以块为单位进行,例如,即使你想利用 1 个字节,最小分配也应该始终是 2 的幂,例如,MFC 中的 CString 使用 128 个字节,或 64 个字节,通过这样做这样你肯定会浪费一些空间,但也减少了计算量。如果您的分配是基于块完成的,那么您可以使用块大小和当前指针值来使用最大偏移量以避免溢出。

【讨论】:

【参考方案8】:

对于地址溢出,我知道用于时间训练的有用技巧也适用于您的情况。

如果你的平台是 32 位,那么每个地址都是 32 位宽(无符号长型),你可以试试下面的宏:

#define address_after(a,b) ((long)(b) - (long)(a)

#define address_before(a,b) address_after(b,a)

只有当 |m-i|

【讨论】:

以上是关于当我将它添加到指针时,我可以安全地忽略溢出检查的最小偏移量是多少?的主要内容,如果未能解决你的问题,请参考以下文章

jQuery插件可以在localhost上运行,但在我将它发布到服务器时则不行

代码在我的系统上运行良好,但是当我将它提交给应该检查它的机器人时会导致堆栈粉碎错误

添加无符号偏移量

本地连接很好,但是当我将它部署到远程服务器时没有任何反应

在我将它用作 Xcode 中的应用程序图标之前,如何检查图标是不是具有透明度

当 Jupyter 笔记本在一个分支上运行时,如何安全地切换 Git 分支?