setjmp/longjmp与局部变量

32

我的问题涉及到setjmp/longjmp在处理局部变量时的行为。

示例代码:

jmp_buf env;


void abc()
{
  int error;

  ... 
  if(error)
    longjmp(env);
}


void xyz() {
  int v1;           // non-volatile; changed between setjmp and longjmp
  int v2;           // non-volatile; not changed between setjmp and longjmp
  volatile int v3;  // volatile;     changed between setjmp and longjmp
  volatile int v4;  // volatile;     not changed between setjmp and longjmp 

  ...

  if(setjmp(env)) {
    // error handling
    ...
    return;
  }

  v1++; // change v1
  v3++; // change v3

  abc();
}


int main(...) {
  xyz();
}

setjmp/longjmp的文档中写道:

"除了被调用longjmp()函数之前,由包含调用相应setjmp()的函数本地声明且没有易失性限定符类型以及在setjmp()调用和longjmp()调用之间发生更改的自动存储持续期对象的值不确定之外,所有可访问的对象都具有longjmp()调用时的值。"

我看到以下两种可能的解释:

解释1:

本地变量被还原,除非它们同时满足以下两个条件:

  • 非易失性,且
  • 已经更改

解释2:

本地变量被还原,除了:

  • 非易失性的变量,以及
  • 已经更改的变量

根据解释1,在longjmp之后只有v1是未定义的。v2、v3、v4是定义的。 根据解释2,在longjmp之后只有v4是定义的。v1、v2、v3是未定义的。

哪一个是正确的?

顺便说一下:我需要一个通用(“可移植”)答案,适用于所有编译器,即尝试使用特定编译器并没有帮助。


1
实现注意事项:被更改且非易失性的变量可能会保持在longjmp时的状态,也可能会根据代码生成恢复到setjmp时的状态,因此是“不确定的”。因此,如果它们没有被更改,这两个值是相同的,这就是为什么未更改的变量是安全的原因。 - greggo
2个回答

39

setjmp/longjmp通过在第一次调用时保存寄存器(包括堆栈、代码指针等)来实现,并在跳转时恢复。

未标记为'volatile'的自动变量(也称为“本地”,分配在堆栈上的变量)可能会被保存在寄存器中,而不是在堆栈上。

在这种情况下,longjmp将这些寄存器变量还原为调用setjmp()时的值。

此外,一个特别聪明的编译器可能会避免可以从另一个变量的状态推断出的变量,并在需要时进行计算。

然而,如果变量是自动的但没有被分配到寄存器中,则它可能会被在setjmp和longjmp之间的代码更改。

'volatile'显式告诉编译器不要将变量存储在寄存器中。

因此,除非你显式地声明一个变量为'volatile',否则如果你在setjmp/longjmp之间修改了变量,它的值将取决于编译器的选择,因此你不能依赖它('indeterminate')。


我猜相反的情况也是成立的,声明为“register”的自动变量可能存储在寄存器中,但编译器可能会忽略这个提示,因此不能保证它们将被恢复。 - Michael
1
@Michael 实际上,甚至没有保证任何东西被“恢复”。'正常'情况是,在调用导致longjmp的任何函数之前,变量将具有它们的值。如上所述,在某些情况下,它们可能会被setjmp/longjmp执行的所有调用保存寄存器的盲目恢复所覆盖。实际上,这意味着,如果被覆盖,它们可能会被恢复为'setjmp'值,但语言仅说明它们是不确定的,而不是您最后设置的值。 - greggo

18

解释1是正确的。如果原始文本想要的是解释2,那么它将使用"or which are changed"而不是"and"。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接