由于未定义的行为或编译器错误而导致C++代码崩溃
Crash in C++ code due to undefined behaviour or compiler bug?
我遇到了奇怪的崩溃。我想知道这是我的代码还是编译器中的错误。当我使用 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中。也许优化器只是以某种方式发生了变化,我的代码不再触发错误。(该错误似乎非常微妙,当我删除
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;
,这完全没问题。
根据标准的 §5.7p4,c.p
现在表现为一个仅包含 v1
的元素数组的单端:
对于这些运算符 [
+
和-
],指向 非数组对象的行为与指向 长度为 1 的数组,对象类型作为其元素 类型。
然后*(--c)
将该指针移回&v1
并取消引用它,这也很好。
它不一定是UB或编译器错误。这两者都不是由于VS2010的生产方式。
严格来说,您的程序表现出明确定义的行为。但是,这可能仅根据最新的C++标准。VS2010仅针对可能未包含此条款的标准草案实施。如果没有,那么你的代码不是 UB,但 VS 生成 UB 并不是不正确的,因为这些是制作它时的要求。
当然,如果在 C++03 中将堆栈对象视为一个对象的数组是合法的,那么这是一个编译器错误。
编辑:如果您仍然如您所说的那样遇到数组崩溃,那么这绝对是一个编译器错误。
您将&v1
带入 c.p,然后使用运算符 ++ 推进它。您不能依赖堆栈的顺序,因此会出现未定义的行为((&v1)+1 != &v2
- 当回溯以零开始时,如何调试崩溃
- 内联映射初始化的动态atexit析构函数崩溃
- 执行函数时导致崩溃的变量
- 程序崩溃并显示"std::out_of_range"错误
- CoInitialize()在单独的线程上崩溃而不返回
- 使用调试/崩溃报告将应用程序部署到客户端
- 为什么所有C++编译器都会崩溃或挂起此代码
- 为什么lambda在clang上崩溃而不是在gcc上崩溃
- 为什么我的多线程作业队列崩溃
- ExtractIconEx:可以工作,但偶尔会崩溃
- 为什么引用传递会导致此崩溃(C++)
- 试图创建流或fopen时程序崩溃
- 类对象数组的问题会导致崩溃
- 排序时无法执行交换操作.我做的时候它会崩溃.为什么
- 为什么要增加导致崩溃的指针
- 在虚幻引擎中删除NXOpen对象时崩溃
- 为什么它只打印双链接列表的第一个值,而我的程序却崩溃了
- 应用程序崩溃并显示"symbol _ZdlPvm, version Qt_5 not defined in file libQt5Core.so.5 with link time reference"
- Visual Studio在尝试读取resource.txt文件时崩溃
- 将方法转换为调用该方法的成员函子对象会导致崩溃