崩溃在C ++代码由于未定义的行为或编译器错误? [英] Crash in C++ code due to undefined behaviour or compiler bug?

查看:136
本文介绍了崩溃在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)); //崩溃! (取消引用NULL指针)
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 ix;
}
00CE101F mov esp,ebp
00CE1021 pop ebp
00CE1022 ret


b $ b

在偏移00CE1008它写一个0到x。



在偏移00CE100B它读取x(0)到ecx



在偏移00CE1017它解除引用0指针。



我看到两个可能的原因:


$ b b

  • 在我的代码
    中有一些微妙的(或者不那么微妙?)一些未定义的行为,编译器将这个未定义的行为优化为崩溃。





  • 谢谢你,



    编辑:处理有关指向无效位置的指针的评论



    如果我更改 v1 struct tup v1 [10]; 并设置 cp =& v1 [0] ,那么将没有指向无效位置的指针。但我仍然可以观察到相同的行为。反汇编看起来有点不同,但仍然有一个崩溃,它仍然是由加载0到ecx和解除引用它。



    编辑:结论



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

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

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

    正如bames53告诉我们,VS2011中不会发生崩溃,并且得出结论: 。



    无论如何,我决定提交该错误有两个原因:




      <

      该错误可能仍然存在于VS2011。也许优化器只是改变了我的代码不触发错误的方式。 (该bug看起来很微妙,当我删除 volative virtual void Reset()


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




    以下是链接:



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

    解决方案

    。这是一个编译器错误。



    代码 *(c ++)= v2 > cp 产生原始值。该值在上一行中分配,为& v1 。所以,实际上,它 v1 = v2; ,这是完全正常。



    cp 现在表现为一个元素数组的一个过去的结束,只保存 v1 ,根据标准§5.7p4 :


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


    然后 *( - c)该指针返回& v1 并解除引用,这也很好。


    I am experiencing strange crashes. And I wonder whether it is a bug in my code, or the compiler. When I compile the following C++ code with Microsoft Visual Studio 2010 as an optimized release build, it crashes in the marked line:

    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;
    }
    

    Looking into the disassembly, it's obvious that it must crash:

    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  
    

    At offset 00CE1008 it writes a 0 into x.

    At offset 00CE100B it reads x (the 0) into ecx

    At offset 00CE1017 it dereferences that 0-pointer.

    I see two possible reasons:

    • Either there is some subtle (or not so subtle?) case of undefined behaviour in my code and the compiler "optimizes" this undefined behaviour into a crash.

    • or there is a compiler bug

    Does anyone see what might cause the problem?

    Thank you,

    Jonas

    EDIT: To address the comments regarding "pointer to invalid location"

    If I change v1 to be struct tup v1[10]; and set c.p = &v1[0];, then there will be no pointer to an invalid location. But I can still observe the same behaviour. The disassembly looks marginally different, but there is still a crash and it is still caused by loading 0 into ecx and dereferencing it.

    EDIT: Conclusion

    So, probably it is a bug. I found out that the crash vanishes if I change

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

    to

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

    As bames53 tells us, the crash does not occur in VS2011 and concludes that it must have been fixed.

    Nontheless, I decided to file that bug for two reasons:

    • The bug might still be present in VS2011. Maybe the optimizer just has changed in a way that my code doesn't trigger the bug anymore. (the bug seems to be very subtle, it doesn't occur when I remove the volative or the virtual void Reset())

    • I want to know if my workaround is a reliable way to rule out the crashes, or if code changes in other places can reintroduce the bug.

    Here is the link:

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

    解决方案

    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.

    这篇关于崩溃在C ++代码由于未定义的行为或编译器错误?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆