c-使用带有本地jmp_buf的setjmp和longjmp



如果本地jmp_buf实际上由寄存器而非堆栈内存表示,那么当setjmplongjmp返回时,setjmplongjmp是否可能导致本地jmp_buf的内容不确定?


建议的重复是否允许对一个setjmp()调用多次执行longjmp(()?在全局变量的上下文中询问。有人建议,由于答案解释了变量的修改方式不会阻止其随后被调用,这也充分回答了局部变量的问题
但是对局部变量的处理不同于全局变量。特别地,如果本地jmp_buf变量实际上保存在寄存器中而不是存储器中,则在longjmp之后的恢复可能不会呈现可重用的jmp_buf变量


作为一项学术练习,我试图用setjmp代替goto。为了使循环替换保持为函数的局部变量,所使用的jmp_buf也是一个局部变量。

void foo (int n) {
jmp_buf jb;
volatile int i;
i = setjmp(jb);
if (i < n) {
do_stuff(i);
longjmp(jb, ++i);
}
}

我知道在setjmp调用和longjmp调用之间修改的非易失性局部变量在longjmp之后未指定。然而,我对本地jmp_buf变量本身很好奇,特别是在jmp_buf变量由寄存器而不是堆栈上的内存表示的情况下。

目前尚不清楚longjmp本身是否可以被认为是可以修改本地jmp_buf变量的东西,以及这是否意味着当setjmp在调用longjmp后返回时,其内容是未指定的。

我以为我可以通过将jb声明为volatile来轻松地调度问题,但这触发了一个警告(我将其视为错误):

... error: passing argument 1 of ‘_setjmp’ discards ‘volatile’ qualifier from pointer target type [-Werror=discarded-qualifiers]
setjmp(jb);
^~

此外,setjmp的规范并没有说明它是像设置jmp_buf之后还是设置jmp_buf之前那样保存寄存器值。

如果我需要关心它,我可以创建一个jmp_buf的易失性副本,并四处复制它的内容。但是,如果不需要的话,我想避免这种情况。

C11标准章节§7.13.2.1第3点规定:

在调用longjmp函数时,所有可访问的对象都有值,抽象机的所有其他组件都有状态,除了对于包含调用不具有volatile限定类型的相应setjmp宏并且在CCD_ 31调用和CCD_不确定。

您的jmp_buf对象不会在setjmp(jb)longjmp(jb, ++i)之间更改。在调用之间唯一更改的变量是i,正如标准所建议的,它被声明为volatile

因此,为了回答您的问题,longjmp本身不能";修改本地CCD_ 39的内容[以这样的方式]使得当CCD_;,但是通过其他方式修改两个呼叫之间的CCD_ 41肯定会引起麻烦。

没关系。

与此相关的是,i上不需要volatile,因为它是由setjmp()分配的。

仔细阅读CCD_ 45的手册页和我的K&RC,jb的内容仅在函数体中无效,这意味着如果对longjmp()进行第二次调用,它将看到jb的有效视图。在有效代码在较新的标准版本中不会变为无效的假设下,这一点今天仍然适用。

TL;DR您不需要将类型为jmp_buf的变量标记为volatile。

TL;DR 由于标准不明确,最好将局部jmp_buf的值视为局部longjmp之后的不确定值

ISO/IEC 9899:2018§17.13.1.1¶2描述了setjmp的行为,¶3描述了返回时发生的情况。

setjmp宏将其调用环境保存在其jmp_buf参数中,供longjmp函数稍后使用

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

我们推断setjmp的成功返回会导致初始化的jmp_buf参数。然而,没有提到初始化是否考虑到jmp_buf本身具有自动存储持续时间(因此,它本身可以由寄存器而不是存储器表示)。

ISO/IEC 9899:2018§7.13.2.1¶3描述了longjmp的行为,其措辞与Marko:引用的2011年文本相同

所有可访问的对象都有值,而抽象机的所有其他组件(254)都有状态,截至调用longjmp函数时,除了自动存储对象的值包含相应setjmp宏调用的函数的本地持续时间不具有volatile限定类型并且在setjmp调用和longjmp调用不确定。


254)这包括但不限于浮点状态标志和打开文件的状态

然而,之间的单词的含义有些难以捉摸。该标准本可以明确规定之间的上下文为setjmp完成后的均值。例如,措辞可以说明:

。。。在setjmp返回longjmp调用之间的更改是不确定的。

当前的措辞建议应该包括setjmp本身的调用,作为可能触发不确定条件的东西。

然而,longjmp返回的语义可能涵盖了这个问题。ISO/IEC 9899:2018§17.13.2.1¶4规定:

longjmp完成后,线程执行继续进行,就好像setjmp宏刚刚返回了val指定的值。。。

这句话可以解释为setjmp的调用语义是相同的,无论它是从直接调用返回还是从longjmp函数返回。也就是说,setjmp的返回意味着jmp_buf参数被初始化,并且可以由另一个longjmp使用。但同样,这一点还不清楚。在最具限制性的解释中,like子句只对setjmp返回的值进行说明,而不对调用本身进行说明。

由于语义不明确,因此在从longjmp返回时,将jmp_buf对象值视为不确定是合适的。

最新更新