由于未定义的行为或编译器错误导致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;
}

看看拆卸,显然它必须崩溃:

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并将其解引用来引起的。

编辑:结论

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

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

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

bames53告诉我们,在VS2011中不会发生崩溃,并得出结论,它必须被修复。

不过,我决定提交这个错误有两个原因:

>该错误可能仍然存在于VS2011。也许优化器已经改变了,我的代码不会再触发bug了。 (这个bug似乎是非常微妙的,当我删除volative或virtual void Reset())时不会发生
>我想知道我的解决方法是否是排除崩溃的可靠方法,或者如果其他地方的代码更改可能会重新引入错误。

链接在这里:

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

最佳答案
代码很好这是一个编译器错误。

代码*(c)= v2将递增c.p产生原始值。该值在上一行中分配,并且是& v1。所以,实际上,它是v1 = v2,这是非常好的。

c.p现在表现为一个元素数组的一个过去的结尾,该数组只保存标准中的每个§5.7p4的v1;

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.

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

转载注明原文:由于未定义的行为或编译器错误导致C代码崩溃? - 代码日志