6

以下代码:

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

主.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,程序运行正常。
  • 优化标志无效(总是崩溃)。

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

我的问题 :

  • 这是错误/错误/错误的代码吗?还是 MinGW 4.5 对代码处理不当?(责备该工具是苛刻和冒昧的,但我怀疑其中的一些设置)。

感谢您的任何建议。

如有必要,请重新标记。

4

3 回答 3

3

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

调用 setjmp() 例程的例程返回后,不得调用 longjmp() 例程

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

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

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

该问题提到在用 C++ 编写的程序中处理遗留 C 代码。在审查 Boost 库之一期间,对 jpeg 库中的 sjlj 问题进行了有趣的讨论。讨论很长,但这里是推荐选项的精髓

于 2011-10-29T13:00:54.123 回答
1

C++ 只对 的使用做了一个额外的限制longjmp()

如果任何自动对象将被程序中将控制转移到另一个(目标)点的抛出异常破坏,则在将控制转移到同一(目标)点的抛出点调用 longjmp(jbuf, val) 具有未定义的行为. (18.7)

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

于 2011-10-29T13:19:20.790 回答
0

longjmp and setjmp 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)

于 2011-10-29T12:45:11.613 回答