由于未定义的行为或编译器错误导致C++代码崩溃?

m3t*_*n0b 16 c++ crash visual-studio-2010 compiler-optimization visual-c++

我遇到了奇怪的崩溃.我想知道它是否是我的代码或编译器中的错误.当我使用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;
}
Run Code Online (Sandbox Code Playgroud)

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

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  
Run Code Online (Sandbox Code Playgroud)

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

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

在偏移00CE1017时,它取消引用0指针.

我看到两个可能的原因:

  • 在我的代码中有一些微妙的(或不那么微妙的)未定义行为的情况,并且编译器将这个未定义的行为"优化"为崩溃.

  • 或者有一个编译器错误

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

谢谢,

乔纳斯

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

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

编辑:结论

所以,可能是一个bug.我发现如果我改变,崩溃就会消失

struct tup* operator--() { return --p; }
Run Code Online (Sandbox Code Playgroud)

struct tup* operator--() { --p; return p; }
Run Code Online (Sandbox Code Playgroud)

正如bames53告诉我们的那样,VS2011中没有发生崩溃,并得出结论它必须已经修复.

尽管如此,我决定提交该bug有两个原因:

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

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

链接在这里:

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

R. *_*des 17

代码很好.这是一个编译器错误.

代码*(c++) = v2将后递增,c.p产生原始值.该值在前一行中分配,并且是&v1.所以,实际上,确实v1 = v2;很好.

c.p现在表现为仅v1按照标准的§5.7p4 保存的单元素数组的一个结尾:

出于这些运算符[ +-] 的目的,指向非阵列对象的指针与指向长度为1的数组的第一个元素的指针的行为相同,其中对象的类型为其元素类型.

然后*(--c)将指针移回&v1并取消引用它,这也没关系.

  • 看起来很可能; 用(cp)++/ - (cp)替换c ++/ - c可以消除vs10中的崩溃,而vs11不会出现崩溃. (2认同)