【问题标题】:How to safely get the return value of setjmp如何安全获取setjmp的返回值
【发布时间】:2014-04-06 11:08:49
【问题描述】:

我想使用 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;
  }
}

多了一个变量,好像不是很好……

【问题讨论】:

  • 如果这一切都是真的,那么很明显,列出所有可能性的全部目的是让您知道您无法存储返回值......所以您已经知道答案了。
  • 伤心,这几乎毫无意义。我只想进行一般清理,并将 error_code 传递给另一个编译单元,当然不想 longjmp 到另一个编译单元。在我之前没有人遇到过这个问题??我做错了什么吗?

标签: c setjmp


【解决方案1】:

来自C99 rationale

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

C89 委员会考虑的替代提案是要求实现 认识到调用 setjmp 是一种特殊情况,因此他们采取了任何预防措施 在 longjmp 调用时正确恢复 setjmp 环境是必要的。本提案 以一致性为由拒绝:目前允许实现 库函数特殊,但没有其他情况需要特殊处理。

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

【讨论】:

  • 我希望标准能够定义宏来指示编译器是否支持许多编译器可以毫无困难地支持但有些不能支持的结构,并定义一个程序的“合规性”术语,该术语需要实现的功能不需要支持,但只在支持它们的实现上使用它们(并且可能拒绝对那些不支持的实现进行编译)。恕我直言,这将比目前许多类型的代码实际上无法满足的术语“严格兼容”更有用。
【解决方案2】:

POSIX.1 确实指定了这些限制。但是,在 Linux 上,setjmp(3) 没有提及它们,因此在该平台上您可以简单地这样做:

int retval = setjmp(jb);

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

【讨论】:

  • 我在 C99 中读过。还使用谷歌找到了这个:pubs.opengroup.org/onlinepubs/009604599/functions/setjmp.html
  • 我将编辑问题以提及 C99 。我希望即使在 Windows 或未来版本的 windows 和 GNU 上也能这样。
  • 你检查过C11吗?
  • 我现在查了一下,运气不好,在 7.31.1 下找到了。
  • 这是非常错误的。 setjmp() 的“返回”值不能用于赋值(或初始化器),只能用于测试(if() 或 switch())
猜你喜欢
  • 2015-12-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多