1

对于实际问题,请跳至问题部分。有关未定义行为的有趣真实示例,请继续阅读:)

有这样的枚举:

struct EnumStruct 
{
   enum Enum
   {
       val0 = 0,
       val1,
       val2,
       val3,
       val4
   };
};

在某些功能中,我们有这个:

const int arrayCount = 6;
int arr[] = {
        EnumStruct::val0,
        EnumStruct::val1,
        EnumStruct::val2,
        EnumStruct::val3,
        EnumStruct::val4
        InvalidValue
      };    

然后有一个循环将arrayCount元素arr放入文件中。这是Prepare()单元测试的例程。并且单元测试应该检查InvalidValue文件中是否存在。我被分配了一个缺陷,指出单元测试失败。不过,它在我的机器上运行完美。经过几个小时的调试后,我注意到它InvalidValue实际上是#defined as -1,并且后面缺少逗号val4。您只能想象在编写该代码的人的地址中从我口中说出的脏话(实际上,它完美运行了 3 年多)。

现在,如您所见,数组实际上由 5 个值组成 -0, 1, 2, 3, 3但循环还将第 6 个元素写入文件,这当然是未定义的行为。现在,虽然从技术上讲它未定义的,但在使用 MSVC 的 Windows 上不会发生崩溃 - 它只是写入该内存位置的垃圾。问题是,如果垃圾恰好不是0, 1, 2, 3, or 4,则单元测试将成功。

问题.vcproj: UT 的文件似乎在我不知道他们是怎么做到的,但是他们的构建越界数组元素总是 0。在我看来,整个虚拟内存在程序执行之前设置为 0。那是什么项目设置?还是我在想象事情?我的意思是,如果只是运气好,有一个 0 超出了数组的范围,那么在多次执行时我的运气就会失败,不是吗?但它总是 0 ......我很困惑。顺便说一句,当我构建同一个项目时,越界元素在每次执行时总是具有不同的值。你能解释一下吗?谢谢。

4

2 回答 2

0

正如您所说,它是未定义的,因此实现者可以自由地做任何他们喜欢的事情。我根本不能代表 Visual C++,但我知道其他产品在运行调试版本时会执行诸如将内存归零之类的操作,这样无效指针取消引用之类的操作将在故障点失败。我猜微软可能正在做类似的事情。

于 2011-07-05T14:25:28.087 回答
0

实际问题是内存在开始时总是为0吗?好吧,这可能取决于。通常,当操作系统为您提供一页内存时,它将被清除(作为一种安全措施,因此您无法读取任何其他进程在其内存中的值),因此在许多情况下您会发现未初始化的可以看起来像 0,直到内存在您自己的进程中被重用,在那里您将获得您之前编写的任何内容。

还有一些编译器标志会影响它。为了检测未初始化的内存问题,有时调试版本会在从操作系统分配内存之后并将其处理给程序之前将模式写入内存,并且在重新分配之前释放程序中的内存(以检测对释放的内存的访问)后会使用不同的模式,所以更容易识别发生了什么(如果您在调试器中看到该值为0xDEADBEEF,您将知道内存已被程序释放。您必须阅读编译器/IDE 文档以获取更准确的信息。

于 2011-07-05T14:32:03.307 回答