16

我正在经历奇怪的崩溃。我想知道这是否是我的代码或编译器中的错误。当我使用 Microsoft Visual Studio 2010 编译以下 C++ 代码作为优化版本构建时,它在标记的行中崩溃:

struct tup { int x; int y; };

class C 
{
public:
  struct tup* p;

  struct tup* operator--() { return --p; }
  struct tup* operator++(int) { return p++; }

  virtual void Reset() { p = 0;}
};

int main ()
{
  C c;
  volatile int x = 0;
  struct tup v1;
  struct tup v2 = {0, x};

  c.p = &v1;
  (*(c++)) = v2;

  struct tup i = (*(--c));   // crash! (dereferencing a NULL-pointer)
  return i.x;
}

查看反汇编,很明显它必须崩溃:

int _tmain(int argc, _TCHAR* argv[])
{
00CE1000  push        ebp  
00CE1001  mov         ebp,esp  
00CE1003  sub         esp,0Ch  
  C c;
  volatile int x = 0;
00CE1006  xor         eax,eax  
00CE1008  mov         dword ptr [x],eax  
  struct tup v1;
  struct tup v2 = {0, x};
00CE100B  mov         ecx,dword ptr [x]  

  c.p = &v1;
  (*(c++)) = v2;
00CE100E  mov         dword ptr [ebp-8],ecx  

  struct tup i = (*(--c));
00CE1011  mov         ecx,dword ptr [x]  
00CE1014  mov         dword ptr [v1],eax  
00CE1017  mov         eax,dword ptr [ecx]  
00CE1019  mov         ecx,dword ptr [ecx+4]  
00CE101C  mov         dword ptr [ebp-8],ecx  
return i.x;
}
00CE101F  mov         esp,ebp  
00CE1021  pop         ebp  
00CE1022  ret  

在偏移量 00CE1008 处,它将 0 写入 x。

在偏移量 00CE100B 处,它将 x(0)读入 ecx

在偏移量 00CE1017 处,它取消引用该 0 指针。

我看到两个可能的原因:

  • 在我的代码中存在一些微妙的(或不那么微妙的?)未定义行为的情况,编译器将这种未定义的行为“优化”为崩溃。

  • 或者存在编译器错误

有谁看到可能导致问题的原因?

谢谢,

乔纳斯

编辑:解决有关“指向无效位置的指针”的评论

如果我更改v1为 bestruct tup v1[10];和 set c.p = &v1[0];,那么将没有指向无效位置的指针。但我仍然可以观察到相同的行为。反汇编看起来略有不同,但仍然存在崩溃,它仍然是由将 0 加载到 ecx 并取消引用它引起的。

编辑:结论

所以,这可能是一个错误。我发现如果我改变,崩溃就会消失

struct tup* operator--() { return --p; }

struct tup* operator--() { --p; return p; }

正如 bames53 告诉我们的那样,在 VS2011 中不会发生崩溃,并得出结论认为它必须已修复。

尽管如此,我还是出于两个原因决定提交该错误:

  • 该错误可能仍然存在于 VS2011 中。也许优化器只是改变了我的代码不再触发错误的方式。(该错误似乎非常微妙,当我删除volative或时不会发生virtual void Reset()

  • 我想知道我的解决方法是否是排除崩溃的可靠方法,或者其他地方的代码更改是否可以重新引入错误。

链接在这里:

https://connect.microsoft.com/VisualStudio/feedback/details/741628/error-in-code-generation-for-x86

4

3 回答 3

17

The code is fine. It's a compiler bug.

The code *(c++) = v2 will post-increment c.p yielding the original value. That value was assigned in the previous line and is &v1. So, in effect, it does v1 = v2;, which is perfectly fine.

c.p now behaves as a one-past-the-end of a one element array that holds only v1, per §5.7p4 of the standard:

For the purposes of these operators [+ and -], a pointer to a nonarray object behaves the same as a pointer to the first element of an array of length one with the type of the object as its element type.

Then *(--c) moves that pointer back to &v1 and dereferences it, which is also fine.

于 2012-05-10T17:54:01.337 回答
1

它不一定是 UB 或编译器错误。两者都不是由于 VS2010 的生产方式。

Strictly speaking, your program exhibits well-defined behaviour. However, that might only be according to the newest C++ Standard. VS2010 is only implemented against a draft Standard which may not have included this provision. If it did not, then your code is not UB, but VS is not incorrect to produce UB, as those were the requirements of the time it was made.

Of course, if it's been legal to treat stack objects as an array of one object in C++03, then it is a compiler bug.

Edit: If you still get the crash for an array as you state, then that is definitely a compiler bug.

于 2012-05-10T17:39:18.650 回答
-3

&v1进入 cp ,然后使用运算符 ++ 推进它。您不能依赖堆栈的顺序,因此会出现未定义的行为 ( (&v1)+1 != &v2)

于 2012-05-10T17:25:44.857 回答