如何安全地获取setjmp的返回值

时间:2014-04-06 11:08:50

标签: c longjmp setjmp

我想使用longjmp返回错误代码,并从调用setjmp的函数传递它。简化代码:

int do_things(stuff ........)
{
 int error_code;
 jmp_buf jb;

 if ((error_code = setjmp(jb)) == 0) {
    /* do stuff */
    return 0;
 }
 else {
    return error_code;
 }
}

但是我读到了:  " setjmp宏的调用只能出现在以下某个上下文中:"

 the entire controlling expression of a selection or iteration statement

if (setjmp(jb)) {
switch (setjmp(jb)) {
while (setjmp(jb)) {

 one operand of a relational or equality operator with the other operand
 an integer constant expression, with the resulting expression being
 the entire controlling expression of a selection or iteration statement

if (setjmp(jb) < 3) {

 the operand of a unary ! operator with the resulting
 expression being the entire controlling expression of a
 selection or iteration statement

if (!setjmp(jb)) {

 the entire expression of an expression statement (possibly cast to void).

setjmp(bf); 

获得返回值有一个很好的方法吗? (不使用switch,并为所有可能的值写case

编辑

感谢Matt在c99理由中找到它。 我现在想出的是:

int do_things(stuff ........)
{
  volatile error_code;
  jmp_buf jb;

  if (setjmp(jb) == 0) {
     working_some(&error_code, ....);
     working_again(&error_code, ....);
     working_more(&error_code, ....);
     working_for_fun(&error_code, ....);
     return 0;
  }
  else {
     general_cleanup();
     return error_code;
  }
}

还有一个变量,看起来不太好......

2 个答案:

答案 0 :(得分:3)

来自C99 rationale

  

对setjmp提出的一个要求是它可以像任何其他函数一样使用,也就是它   可以在任何表达式上下文中调用,并且表达式正确计算是否正确   从setjmp返回是直接的或通过调用longjmp。不幸的是,任何实施   setjmp作为一个传统的被调用函数,对调用环境不够了解   保存在表达式评估中使用的任何临时寄存器或动态堆栈位置。 (setjmp宏似乎只有在它扩展为内联汇编代码或调用时才有帮助   一个特殊的内置函数。)初始调用setjmp时临时可能是正确的,但是   不太可能在相应的longjmp调用启动的任何返回上。这些   考虑因素决定了仅在相当简单的范围内调用setjmp的约束   表达式,不太可能需要临时存储。

     

C89委员会考虑的另一项提案是要求实施   认识到调用setjmp是一种特殊情况,因此他们采取任何预防措施   在longjmp调用时正确恢复setjmp环境是必要的。这个提议   因一致性而被拒绝:目前允许实施    图书馆功能特别,但没有其他情况要求特殊待遇。

我对此的解释是,它被认为过于严格,无法指定a = setjmp(jb);必须有效。所以标准没有定义。但是特定的编译器可能会选择支持这个(并且希望能够记录它)。为了便于移植,我想您应该使用一些预处理器检查来验证代码是否使用已知支持此编译器的编译器进行编译。

答案 1 :(得分:0)

POSIX.1确实指明了这些限制。但是,在Linux上,setjmp(3)并未提及它们,因此在该平台上您只需执行以下操作:

int retval = setjmp(jb);

当然这是以一些便携性为代价的,但我不知道它有多糟糕。