这段代码是否包含隐藏的错误?

时间:2011-10-29 12:14:40

标签: c++ mingw setjmp

以下代码:

  • 使用gcc版本4.4.5编译时运行正常(Ubuntu / Linaro 4.4.4-14ubuntu5 / 32bits)
  • 使用MSVC10(Win7 / 32bits)
  • 编译时运行正常
  • 使用gcc版本4.5.2(Win7 / 32bits上的MinGW)
  • 运行时崩溃

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函数调用,程序运行正常。
  • 如果删除try{ ... } catch(...){ }内的do_work块,程序运行正常。
  • 优化标志无效(始终崩溃)。

我知道C ++代码中的setjmp/longjmp问题,但是我不得不使用它来与一些遗留的C代码进行交互。

我的问题:

  • 这是错误/错误/错误的代码吗?或者是MinGW 4.5错误处理代码? (责备这个工具是苛刻和冒昧的,但我怀疑它有一些设置。)

感谢您的任何建议。

请在必要时重新加注。

3 个答案:

答案 0 :(得分:3)

Unix上的longjmp(3)手册页说:

  

在例程之后可能不会调用longjmp()例程   调用setjmp()例程返回

我认为它解释了您对“有时控制永远不会达到BP1 ”的担忧。 我不认为“运行良好”是可靠的判断。我宁愿期望它随机运行良好,通常会搞乱堆栈。

在将longjmp / setjmp与C ++异常混合时,应该考虑一些明确的建议,以避免崩溃和未定义的行为:

  • 不要在C ++程序中使用setjmp / longjmp。
  • 如果在可能发生异常的程序中使用setjmp / longjmp函数,只要它们不进行交互,您就是安全的。
  • 永远不要进入或退出try子句和catch子句。
  • 永远不要超过自动对象的初始化点。
  • 永远不要长时间破坏破坏自动对象,特别是如果析构函数不重要。
  • 永远不要从信号处理程序中抛出。
  • 永远不要从嵌套信号处理程序调用longjmp。
  • longjmp从位置X到位置Y的行为保持可预测和有效,只要在X处抛出并在X处捕获的异常具有相同的效果。
  • 如果将setjmp / longjmp与异常混合使用,请不要指望可移植性。
  • 相反,请参阅您正在使用的文档编译器中的相关详细信息。例如,如果您使用Visual C ++,请阅读Use setjmp/longjmp

问题提到在用C ++编写的程序中处理遗留C代码。在审查其中一个Boost库时,jpeg库中有关于sjlj问题的有趣讨论。讨论很长,但这里是essence with recommended options

答案 1 :(得分:1)

C ++仅对longjmp()的使用提出了一个额外的限制:

如果任何自动对象被抛出的异常将控制权转移到程序中的另一个(目标)点,那么在抛出点调用longjmp(jbuf,val)将控制转移到相同的位置(目的地)点有未定义的行为。(18.7)

您似乎意识到了这一点,并且您的程序没有这样做。我投票编译器缺陷。

答案 2 :(得分:0)

longjmpsetjmp是c函数,使用C ++异常处理语义调用它们,对象销毁语义是未定义的行为,这意味着它是否适用于实现(您的测试显示了这一点) ,SEH堆栈展开语义破坏事物,取决于使用的类型,iirc你的工作人员使用dwarf2)