这段代码是否包含隐藏的错误?
以下代码:
- 使用 gcc 版本 4.4.5 (Ubuntu/Linaro 4.4.4-14ubuntu5/32bits)
- 编译时运行良好 使用 MSVC10 (Win7/32bits) 编译时运行良好
- 使用 gcc 版本 4.5.2 运行时崩溃 (Win7 上的 MinGW) /32bits)
main.cpp :
# include <iostream>
# include <csetjmp>
# include <stdexcept>
using namespace std ;
void do_work(jmp_buf context)
{
try
{
throw runtime_error("Ouch !") ;
}
catch(exception & e)
{
}
longjmp(context, -1) ; //BP1
}
int main(int, char *[])
{
jmp_buf context ;
try
{
if( setjmp(context) != 0 )
{
throw runtime_error("Oops !") ; //BP2
}
do_work(context) ;
}
catch(exception & e)
{
cout << "Caught an exception saying : " << e.what() << endl ;
}
}
我尝试调试它,但程序的行为很奇怪。有时我可能会越过第一个断点 (BP1),然后在 BP2 处崩溃,有时控制永远不会到达 BP1,就像程序陷入无限循环一样。以我的调试能力,我无法说更多。
这段代码是我能得到的最小代码,它表现出了 MinGW 4.5 的奇怪行为。我还注意到:
- 如果我用其内容替换 do_work 函数调用,程序运行正常。
- 如果我删除
do_work
内的try{ ... } catch(...){ }
块,程序运行正常。 - 优化标志无效(总是崩溃)。
我知道 C++ 代码中的 setjmp/longjmp
问题,但我被迫使用它来与一些旧版 C 代码交互。
我的问题:
- 这是一个有缺陷/有缺陷/错误的代码吗?或者 MinGW 4.5 对代码处理不当? (指责该工具是严厉和自以为是的,但我怀疑其中的某些设置)。
感谢您的任何建议。
如有必要,请重新标记。
The following code :
- Runs fine when compiled with gcc version 4.4.5 (Ubuntu/Linaro 4.4.4-14ubuntu5/32bits)
- Runs fine when compiled with MSVC10 (Win7/32bits)
- Crashes when running with gcc version 4.5.2 (MinGW on Win7/32bits)
main.cpp :
# include <iostream>
# include <csetjmp>
# include <stdexcept>
using namespace std ;
void do_work(jmp_buf context)
{
try
{
throw runtime_error("Ouch !") ;
}
catch(exception & e)
{
}
longjmp(context, -1) ; //BP1
}
int main(int, char *[])
{
jmp_buf context ;
try
{
if( setjmp(context) != 0 )
{
throw runtime_error("Oops !") ; //BP2
}
do_work(context) ;
}
catch(exception & e)
{
cout << "Caught an exception saying : " << e.what() << endl ;
}
}
I tried debugging it but the program behaves strangely. Sometimes I could get past the first breakpoint (BP1), then crash at BP2, and sometimes control never reachs BP1, like if the program is stuck in an infinite loop. I cannot say more with my debugging skills.
This code is the minimal I could get that exhibits the strange behavior with MinGW 4.5. I also noticed that :
- If I replace the
do_work
function call by its content, the program runs fine. - If I remove the
try{ ... } catch(...){ }
block insidedo_work
, the program runs fine. - Optimization flags have no effect (always crashing).
I'm aware of the setjmp/longjmp
issues in C++ code, but I'm forced to use it to interface with some legacy C code.
My question :
- Is this a faulty/buggy/erroneous code ? Or is MinGW 4.5 mishandling the code ? (It is harsh and presumptuous to blame the tool, but I suspect some settings in it).
Thanks for any advice.
Please retag if necessary.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
longjmp
和setjmp
是 C 函数,使用 C++ 异常处理语义和对象销毁语义调用它们是未定义的行为,这意味着它是否有效取决于实现(您的测试表明,SEH 堆栈展开语义会破坏事物,具体取决于所使用的类型,iirc 您的工作正在使用 dwarf2)longjmp
andsetjmp
are c functions, invoking them with C++ exception handling semantics and object destruction semantics is undefined behavior, which means its up to the implementation whether it works or not (your testing shows this, the SEH stack unwind semantics break things, depending on the type used, iirc your working ones are using dwarf2)Unix 上的 longjmp(3) 手册页显示:
我认为它解释了您的担忧“有时控制永远不会达到 BP1”。
我不认为“运行良好”是可靠的判断。我宁愿期望它随机运行良好,并且通常会弄乱堆栈。
在将 longjmp/setjmp 与 C++ 异常混合使用时,应考虑一些明确的建议,以避免崩溃和未定义的行为:
问题提到处理用 C++ 编写的程序中的遗留 C 代码。在审查 Boost 库之一期间,对 jpeg 库中的 sjlj 问题进行了有趣的讨论。讨论很长,但这里是带有推荐选项的要点。
The longjmp(3) man page on Unix says:
I think it explains explains your concern that "sometimes control never reachs BP1".
I don't think "runs fine" is reliable judgement. I'd rather expect it randomly runs fine and generally is messing up with stack.
There are a few clear recommendations that should be taken into account while working mixing longjmp/setjmp with C++ exceptions in order to avoid crashes and undefined behaviour:
The question mentions dealing with legacy C code in programs written in C++. There has been an interesting discussion on sjlj issues in jpeg library during review of one of Boost libraries. The discussion was long but here is essence with recommended options.
C++ 仅对
longjmp()
的使用做了一个额外的限制:如果任何自动对象将被抛出的异常破坏,并将控制权转移到程序中的另一个(目标)点,则调用在将控制转移到同一(目标)点的抛出点处的 longjmp(jbuf, val) 具有未定义的行为。 (18.7)
您似乎意识到了这一点,但您的程序没有这样做。我投票编译器缺陷。
C++ only makes one additional restriction on the use of
longjmp()
:If any automatic objects would be destroyed by a thrown exception transferring control to another (destination) point in the program, then a call to longjmp(jbuf, val) at the throw point that transfers control to the same (destination) point has undefined behavior. (18.7)
You seem to be aware of this, and your program doesn't do it. I vote compiler defect.