我在 C++11 并发实现深处的某个地方读取 Visual Studio 2012 中的位置 0xFEEEEF6(使用 2012 年 11 月 CTP 编译器)时遇到了访问冲突。
这个值有什么特殊含义吗?在维基百科中我发现了类似的条目(0xFEEEFEEE和0xFEEDFACE)。
我在 C++11 并发实现深处的某个地方读取 Visual Studio 2012 中的位置 0xFEEEEF6(使用 2012 年 11 月 CTP 编译器)时遇到了访问冲突。
这个值有什么特殊含义吗?在维基百科中我发现了类似的条目(0xFEEEFEEE和0xFEEDFACE)。
0xFEEEFEF6
它本身没有特殊含义,但它可能基于 MSVC 喜欢围绕堆分配放置的“保护字节”段之一。正如 Jan Dvorak 所指出的,它可能在某事的末尾之后 8 个字节,很可能是在数组末尾之后的 2 个指针。
这个概念是你可能会意外访问但不应该访问的内存被标记为非常明显的模式。一些最常见的是0xCDCDCDCD
and 0xFDFDFDFD
,尽管0xDDDDDDDD
and0xFEEEFEEE
也很容易遇到。经典编译器(不确定是否仍然使用它)喜欢0xDEADBEEF
. 这是您将在其中看到保护字节的案例和位置的一个很好的记录。
在段错误(访问冲突)中看到这些的两个最常见原因通常是访问已经释放的内存并超出您的界限,尤其是在指针数组中。用于保护数据的大多数值如果要显示在应用程序中则无效(否则您将不会在0x00000000
or处获得一块内存0xCDCDCDCD
,这些远远超出您的堆所在的虚拟地址空间)。了解常见问题可以节省大量调试时间。
请注意,除了极少数/没有例外,这些保护字节仅显示在调试版本中。每次分配/解除分配时都使用特殊模式写入内存(实际上,写入的内存比分配的内存要多得多,因为大多数保护模式都发生在已分配块的边界上)相当昂贵,不应该在运行时完成。如果您在调试版本中遇到此类问题,您可能会从发布版本中获得看似随机(未定义)的地址。你也可能很不幸,一个合法的地址最终被错误地抓取了,这可能导致各种堆损坏。
因为保护字节不会出现在发布版本中,所以您不能尽可能检查它们并将其用作NULL
代码中的条件。相反,智能指针和容器可以帮助您正确管理内存并从一开始就避免错误访问。虽然有时很烦人,但智能指针非常有助于避免此类问题。请注意,某些类型的访问违规,尤其是缓冲区溢出,被认为是一整类安全漏洞,因为它们出现的频率很高。
如果没有虚拟机/运行时迫使您保持在某些内存限制内,那么如果可以很容易地访问您不应该访问的内存。例如:
int values[10];
int output = 0;
int length = 10;
while (int i <= length) {
output += values[++length];
}
前缀增量将导致您跑出数组的末尾并访问values[10]
无效索引。有时这可能会立即导致访问冲突并停止您的程序,有时它可能是您被允许访问的内存并且该值将被添加到,这可能会导致应用程序的其余部分发生output
溢出和意外行为。output
存在保护字节,以便您的段错误或增量在调试时具有可重复的值并尽可能明显。