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

时间:2009-04-05 09:35:16

标签: c++ c pointers overflow

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

为避免混淆:我在谈论溢出检查,而不是关于边界检查。例如:如果你有一个带有m个字符的字符串开头的指针p,并且你想要在正偏移量i上访问字符,那么你需要检查i< m或你可以间接检查p + i< p + m。但是,在后一种情况下,你还必须确保p + i不会溢出,即你必须确保p + i> = p。

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

Update2:听说任何一个特定的平台已经非常有趣了,即使它不具有普遍性。优选地,不是一些模糊的嵌入式嵌入式。 x86或基于Power的32位Win,Linux和Mac将是最有趣的。

8 个答案:

答案 0 :(得分:8)

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

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

答案 1 :(得分:4)

严格地说答案是0,因为p已经指向一个超过数组末尾的那个,这就是标准所说的有效。

在特定实现中,您可能能够获得一些金额,但这是完全实现定义的。有一些硬件,也许仍然是,检查CPU指令中指针的操作:如果p指向2个整数的数组,执行p+3将导致CPU失败该指令。另一方面,在大多数现有的硬件上你可以侥幸逃脱。

答案 2 :(得分:3)

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

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

如果我们知道你的平台的确切细节(主要是哪个CPU,操作系统,哪个编译器),那么就可以给你一个通常工作的答案,只要没有编译器或操作系统中的更改。

但似乎你的方式错了。如果这是如此性能关键,就像你一直说的那样,安排它以便指针溢出不是问题

严格地说,将任何添加到一个指针,使其指向之前指向的同一块内存,是未定义的。它可能有用,或者它可能在某些架构上表现得非常时髦。它可能会在意外时间溢出,可能会导致一些硬崩溃。这就是为什么语言简单地说“不允许”,以及为什么我们不能说实际上当你没有告诉我们它正在运行的平台时会发生什么。

C ++指针不是内存地址。这就是编译器通常如何实现它,是的,但它们遵循不同的规则。根据CPU指令集,某些内容对于内存地址是合法的,这些指针在指针上是不合法的。

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

答案 3 :(得分:2)

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

在较低级别查看事物时,指针通常实现为(无符号)整数。整数的大小足以容纳任何内存位置的地址。指针溢出的唯一方法是超出地址空间,并且在符合C标准时无法做到。

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

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

答案 4 :(得分:1)

在我的头脑中,似乎这非常依赖于:

  • 操作系统
  • 基础HW
  • 编译器

作为一项规则,只有我记得,堆栈是在线性地址空间的顶部分配的。考虑到您运行了各种运行时库,您的实际数据很可能不会在该空间的顶部运行。无论哪种方式,如果你超过malloc分配的区域,你将遇到其他麻烦,如果你超过你的堆栈框架,你也会遇到麻烦。最重要的是,我认为你不必担心从0xffffffffffffffff到0x0的回绕,但你仍然必须确保你不要超过你的静态,自动或手动分配的内存的界限。

答案 5 :(得分:0)

这取决于两件事

  • 架构
  • 编译器

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

答案 6 :(得分:0)

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

答案 7 :(得分:0)

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

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

  

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

     

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

然后你可以安全地比较地址为address_after(p + i,p + m)只有| m-i | < 0x7FFFFFFF(这是常见的情况)。这个宏很好地处理溢出问题。