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

时间:2009-11-07 11:58:53

标签: c exception-handling stack

我有几个与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,并且在可以引发异常的地方之后调用它。

  6. 感谢。

4 个答案:

答案 0 :(得分:10)

C99规范给出:

  

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

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

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

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

  3. C ++中的
  4. try / catch将在throw和catch之间的所有自动对象上调用析构函数。 setjmp / longjmp不会调用析构函数,因为它们在C中不存在。因此,只要在free的任何内容上调用malloc,就可以自行调用析构函数同时编辑。

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

    #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" );
        }
    }
    

    大致相当于

    #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";
    }
    

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

答案 1 :(得分: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;
}

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

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

答案 2 :(得分:4)

第一部分几乎很简单:当你做一个longjmp时,你最终会在setjmp之后。如果返回值为0,则表示您刚刚执行了setjmp;如果它是非零的,你知道你从其他地方的longjmp到达那里。该信息通常可用于控制代码之后的代码。

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

打电话给longjmp说:“我认为这里有什么问题,帮助,让我离开这里 - 我太困惑了,不能自己清理并通过一堆函数调用返回,如果是的话;只是让我回到在最后一次setjmp之后,世界再次变好了。“

抛出几乎相同的东西,除了它在语法上更清晰,更清晰地支持。此外,虽然longjmp可以带你到程序中的任何位置(无论你在哪里执行setjmp),throw都会在throw的调用层次结构中直接向上结束。

答案 3 :(得分:0)

只是为了添加Pete Kirkham的答案(和评论):由于C标准不允许存储setjmp的返回值,所以也许Pete的例子可以改为使用switch。它仍然演示了如何区分不同的返回值,但不违反标准。

#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(void)
{
  char* data = 0;
  switch(setjmp(env))
  {
    case 0:
    {
      printf("setjmp returned 0\n");
      foo(&data);
      break;
    }
    case 42:
    {
      printf("setjmp returned 42\n");
      handle ( data );
      break;
    }
    default:
    {
      printf("setjmp returned something else?\n");
    }
  }
  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");
  }
}
相关问题