此代码是否包含隐藏的错误

Does this code contain a hidden bug?

本文关键字:错误 隐藏 包含 代码 是否      更新时间:2023-10-16

以下代码:

  • 在gcc版本4.4.5 (Ubuntu/Linaro 4.4.4-14ubuntu5/32bits)下运行良好
  • 使用MSVC10 (win7/32位)编译时运行良好
  • gcc 4.5.2 (MinGW on win7/32位)

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代码接口。

我的问题:

    这是一个错误的/错误的/错误的代码吗?还是mingw4.5错误地处理了代码?(这是苛刻和冒昧的指责工具,但我怀疑其中的一些设置)。

谢谢你的建议。

必要时请重新标记

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

longjmp()例程不能在调用setjmp()例程返回

我认为它解释了你所关心的"有时控制永远达不到BP1"。我不认为"运行良好"是可靠的判断。我更希望它随机运行良好,通常是搞乱堆栈。

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

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

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

c++对longjmp()的使用只有一个额外的限制:

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

你似乎意识到了这一点,而你的程序没有做到这一点。

longjmpsetjmp是c函数,用c++异常处理语义和对象销毁语义调用它们是未定义的行为,这意味着它取决于实现是否有效(您的测试显示了这一点,SEH堆栈展开语义打破了东西,取决于使用的类型,如果您的工作使用的是dwarf2)