崩溃在C ++代码由于未定义的行为或编译器错误? [英] Crash in C++ code due to undefined behaviour or compiler bug?
问题描述
我遇到了奇怪的崩溃。我不知道这是一个错误在我的代码,或编译器。
当我使用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()
)
以下是链接:
。这是一个编译器错误。
代码 *(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 thevirtual 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屋!