C中的异常处理 - setjmp()返回0的用途是什么?

Viv*_*rma 6 c stack exception-handling

我有几个与setjmp/longjmp用法有关的问题 -

  1. setjmp(jmp ___ buf stackVariables)返回0的用途是什么.这是默认值,我们无法影响.

  2. setjmp(stackVariables)的唯一意义是在stackVariables中推送堆栈.基本上0告诉我们堆栈是否已成功推送到stack_variables.

  3. 当你从longjmp返回时,它们有一次是非零值(任何非零).什么是从lomgjmp返回,何时从longjmp返回,处理异常时.这种设置真的令人困惑.

  4. 有些人可以把它与try/throw和catch联系起来.如果可以提供一些很好的setjmp/longjmp示例,那将会非常棒.

  5. longJmp是throw,并且在可以引发异常的地方之后调用它.

谢谢.

Pet*_*ham 10

C99规范给出:

如果返回来自直接调用,则setjmp宏返回值零.如果返回来自对longjmp函数的调用,则setjmp宏返回非零值.

所以1的答案是零表示你setjmp第一次调用,非零表示它从a返回longjmp.

  1. 它推动了当前的程序状态.在longjmp之后,状态恢复,控制返回到它被调用的点,并且返回值不为零.

  2. 在C中没有例外.它类似于fork返回不同的值,这取决于您是在原始流程中,还是在继承环境的第二个流程中,如果您熟悉它.

  3. try/ catch在C++中将调用throw和catch之间的所有自动对象上的析构函数.setjmp/ longjmp不会调用析构函数,因为它们在C中不存在.因此,就free你在malloc同一时间调用任何内容而言,你是独立的.

有了这个附带条件,这个:

#include <stdio.h>
#include <setjmp.h>
#include <string.h>
#include <stdlib.h>

void foo ( char** data ) ;
void handle ( char* data ) ;
jmp_buf env;

int main ()
{
    char* data = 0;

    int res = setjmp ( env ); 
    // stored for demo purposes. 
    // in portable code do not store 
    // the result, but test it directly.

    printf ( "setjmp returned %d\n", res );

    if ( res == 0 )
        foo ( &data );
    else
        handle ( data );

    return 0;
}


void foo ( char** data )
{
    *data = malloc ( 32 );

    printf ( "in foo\n" );

    strcpy ( *data, "Hello World" );

    printf ( "data = %s\n", *data );

    longjmp ( env, 42 );
}

void handle ( char* data )
{
    printf ( "in handler\n" );

    if ( data ) {
        free ( data );
        printf ( "data freed\n" );
    }
}
Run Code Online (Sandbox Code Playgroud)

大致相当于

#include <iostream>

void foo ( ) ;
void handle ( ) ;

int main ()
{
    try {
        foo ();
    } catch (int x) {
        std::cout << "caught " << x << "\n";
        handle ();
    }

    return 0;
}

void foo ( )
{
    printf ( "in foo\n" );

    std::string data = "Hello World";

    std::cout << "data = " << data << "\n";

    throw 42;
}

void handle ( )
{
    std::cout << "in handler\n";
}
Run Code Online (Sandbox Code Playgroud)

在C情况下,你必须进行显式的内存管理(虽然通常你在调用longjmp之前将它释放到malloc中的函数中,因为它使生活更简单)

  • 关键点是setjmp()函数可能返回一次,或者它可能返回几次.它将从'直接调用'(将返回0)返回一次; 任何后续返回都是longjmp()的结果,并将返回非零值. (2认同)
  • 您无法将setjmp()的结果可移植地存储到变量中.C标准说:"_对setjmp宏的调用只出现在以下某个上下文中:选择或迭代语句的整个控制表达式;关系或相等运算符的一个操作数与另一个操作数的整数常量表达式,结果表达式是选择或迭代语句的整个控制表达式;一元!运算符的操作数,结果表达式为[...];或表达式语句[...]的整个表达式." (2认同)

Jac*_*ack 6

setjmp用于将标记放置到longjump的调用应该返回的位置,如果直接调用则返回0,如果调用它则返回1,因为调用了对该setjmp的longjmp.

你必须考虑setjmp就像通常被调用的东西一样,并且在正常操作中不做任何事情(返回0)而返回1并且在调用长跳转时间接调用(并从那里返回).我知道你的意思是混乱,因为它实际上令人困惑.

这是维基百科给出的例子:

#include <stdio.h>
#include <setjmp.h>

static jmp_buf buf;

void second(void)
{
    printf("second\n");         // prints
    longjmp(buf,1);             // jumps back to where setjmp was called - making setjmp now return 1
}

void first(void)
{
    second();
    printf("first\n");          // does not print
}

int main()
{   
    if ( ! setjmp(buf) )
    {
        first();                // when executed, setjmp returns 0
    } 
    else
    {                    // when longjmp jumps back, setjmp returns 1
        printf("main");         // prints
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

你能理解吗?当程序启动时setjmp,在main中执行并返回0(因为它被直接调用),所以first被调用,second然后调用然后它到达longjmp,切换上下文返回到setjmp使用的位置,但这一次,因为它从跳转回来它间接称为函数返回1.

setjmp/longjmp方法的有用之处在于,您可以处理错误情况,而无需在函数调用之间保持标记(特别是当您有很多时,请考虑编译器中的类型检查的递归过程).如果在调用堆栈中的类型检查中出现问题通常你必须返回一个标志并继续返回它以警告调用者类型检查失败.使用longjmp,你只需要出去处理错误而不关心将标志传回去.唯一的问题是,这会强制上下文切换,而不关心堆栈/堆内存的标准释放,因此您应该自己处理它.


Car*_*icz 6

第一部分几乎很简单:当您执行 longjmp 时,您恰好在 setjmp 之后结束。如果返回值为 0,则表示您刚刚执行了 setjmp;如果它不为零,则您知道您是从其他地方的 longjmp 到达那里的。该信息通常有助于控制您的代码在此之后执行的操作。

setjmp/longjmp 是 throw/catch 的老祖宗。setjmp/longjmp 是用 C 定义的,而 throw/catch 是更“现代”的机制,用于在更多面向对象的语言(如 C++)中进行错误恢复。

调用 longjmp 说:“我认为这里有问题,帮助,让我离开这里 - 我太困惑了,无法清理自己并通过一堆函数调用和 if 返回;只是让我回到原来的世界就在最后一次 setjmp 之后,又好了。”

throw 说的几乎是一样的,只是它在语法上得到了更清晰和干净的支持。此外,虽然 longjmp 几乎可以带您到程序中的任何位置(无论您在哪里执行 setjmp),但 throw 最终会在 throw 所在的调用层次结构中直接向上。