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

Crash in C++ code due to undefined behaviour or compiler bug?

本文关键字:C++ 崩溃 代码 错误 编译器 未定义      更新时间:2023-10-16

我遇到了奇怪的崩溃。我想知道这是我的代码还是编译器中的错误。当我使用 Visual Studio 2010 编译以下C++代码作为优化的发布版本时Microsoft它在标记的行中崩溃:

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更改为struct tup v1[10];并设置c.p = &v1[0];,则不会有指向无效位置的指针。但我仍然可以观察到相同的行为。反汇编看起来略有不同,但仍然存在崩溃,它仍然是由于将 0 加载到 ecx 中并取消引用它引起的。

编辑:结论

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

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

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

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

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

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

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

这是链接:

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

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

代码*(c++) = v2将递增后c.p生成原始值。该值是在上一行中分配的,并且&v1 。所以,实际上,它确实v1 = v2;,这完全没问题。

根据标准的 §5.7p4,c.p现在表现为一个仅包含 v1 的元素数组的单端:

对于这些运算符 [ +- ],指向 非数组对象的行为与指向 长度为 1 的数组,对象类型作为其元素 类型。

然后*(--c)将该指针移回&v1并取消引用它,这也很好。

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

严格来说,您的程序表现出明确定义的行为。但是,这可能仅根据最新的C++标准。VS2010仅针对可能未包含此条款的标准草案实施。如果没有,那么你的代码不是 UB,但 VS 生成 UB 并不是不正确的,因为这些是制作它时的要求。

当然,如果在 C++03 中将堆栈对象视为一个对象的数组是合法的,那么这是一个编译器错误。

编辑:如果您仍然如您所说的那样遇到数组崩溃,那么这绝对是一个编译器错误。

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

(