C中的exception处理 – setjmp()返回0的用途是什么?

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

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

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

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

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

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

谢谢。

C99规范给出:

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

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

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

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

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

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

 #include  #include  #include  #include  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  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中的函数中,因为它使生活更简单)

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

您必须将setjmp视为可以正常调用的东西,并且在正常操作中不执行任何操作(返回0),同时返回1并且在调用长跳转时间接调用(并从那里返回)。 我知道你的意思是混乱,因为它实际上令人困惑。

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

 #include  #include  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,您只需出门处理错误,而无需关心将标志传回。 唯一的问题是,这会强制上下文切换,而不关心堆栈/堆内存的标准释放,因此您应该自己处理它。

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

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

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

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

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

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