我经常看到代码添加一个值,例如指针的长度,然后使用这个值,例如
T* end = buffer + bufferLen;//T* + size_t
if (p < end)
但是,缓冲区是否有可能在“buffer + bufferLen”可能溢出的内存末尾附近被分配(例如0xFFFFFFF0 + 0x10),导致“p&lt; end”为假,即使p为有效的元素地址(例如0xFFFFFFF8)。
如果有可能的话,当我看到许多与开始/结束范围一起工作的东西时,如何避免在最后一个元素之后结束下一个元素
答案 0 :(得分:9)
来自标准:
5.9关系运算符[expr.rel]
如果两个指针指向同一个数组或超出的数组 在数组的末尾,指向具有较高下标的对象的指针比较高。
所以你不必担心;一致的实现将确保过去的结束指针与数组的其余部分正确比较。另外,
3.7.4.1分配函数[basic.stc.dynamic.allocation]
[...]返回的指针应适当对齐,以便可以转换 到具有基本对齐要求(3.11)的任何完整对象类型的指针然后使用 访问分配的存储中的对象或数组[...]
这意味着返回的指针应该能够被视为指向适当大小的数组开头的指针,因此5.9继续保持。如果分配函数调用是调用operator new[]
(5.3.4:5)的结果,则会出现这种情况。
实际上,如果你在一个平台上,可以想象分配器(不符合)返回一个以0xFFFFFFFF
结尾的内存块,你可以在大多数情况下写
if (p != end)
答案 1 :(得分:1)
连续内存分配的元素不可能具有非连续的地址。 end
的地址总是高于start
。
如果分配恰好以0xFFFFFFFF结尾,意味着end
将是0x00000000,这将是一个错误,应该修复代码以适应该情况。
在某些平台上虽然这种情况在设计上是不可能的,但为了简单起见,可能是逻辑上的合理折衷。例如,我会毫不犹豫地在Windows用户模式应用程序上编写if(p < end)
。
答案 2 :(得分:1)
是的,在许多[start, end)
对算法端点过去最后一个有效条目。但是您的实现永远不应该取消引用 end
,实际访问的最后一个条目应该是end-1
,这保证在有效区域内。如果您的算法取消引用*end
则是一个错误。事实上,有一些测试分配器故意将该区域放在有效页面的最后几个字节上,然后是一个未分配的区域。对于这样的分配器,解引用*end
的算法将导致保护错误。
打开页面堆调试,验证动态堆内存 操作,包括分配和释放,并导致调试器 在检测到堆错误时中断。
此选项在为图像文件设置时启用整页堆调试 和在系统注册表或内核中设置的标准页堆调试 模式。
整页堆调试(对于/ i)会在页面上放置一个无法访问的页面 分配结束。
标准页面堆调试(针对/ r或/ k)将分配检查为 他们被释放了。
为图像文件设置此标志与键入gflags / p相同 在命令行启用/填充图像文件
对于指针overfllow的问题:没有操作系统分配包含VA地址0xFFFFFFFF的页面,同样没有操作系统分配包含0x00000000的页面。为了发生这种溢出,*start
的大小必须足够大,以使start+1
在有效范围的末尾跳过所有保留的VA。但在这种情况下,为start
分配的地址应至少为以下最后一个有效的VA地址,这意味着start+1
将有效(它跟随{{只要start+N
被分配为start
),1}}也始终有效。
答案 3 :(得分:-1)
不要担心。你的分配器(可能是new
,但也许是别的东西)不会给你一些如此接近它所包含的内存末尾的东西。
担心边界检查。你永远不会得到像这样包装的分配,所以只要你没有超出数组(无论如何都有未定义的行为),你就不会结束了。
注意为内核保留大块进程地址空间也很有用。在大多数操作系统上,此高阶区域是保留的。