在setjmp之前用局部变量的地址调用空函数,为什么?

call empty function with address of local variable before setjmp, what for?

看了C库的代码,看不懂是怎么回事:

struct Foo *foo = NULL;
lib_var((void *)&foo);
if (setjmp(get_jmp_buf()) == 0) {
  foo = ...;
  // other calculation that may cause longjmp
} else {
  //something bad happens
  drop_if_not_null(foo);
}

其中 lib_var 是具有此类原型的函数 void lib_var(void *);, 以及单独的 C 文件中的此类实现:

void lib_var(void *variable)
{
// nothing
}

那么 lib_var 函数呢,比如 volatilesetjmp() != 0 情况下强制编译器从内存中重新读取变量? 这是未定义的行为吗?因为我确信 LTO 删除了这些完全无用的代码, LTO 不应将确认行为更改为标准代码。

是的,从标准 C 的角度来看,这是未定义的行为 - 或者至少,foo 的值是不确定的,不能安全地用于任何事情。

C17 (n2176) 7.13.2.1 (3):

All accessible objects have values, and all other components of the abstract machine have state, as of the time the longjmp function was called, except that the values of objects of automatic storage duration that are local to the function containing the invocation of the corresponding setjmp macro that do not have volatile-qualified type and have been changed between the setjmp invocation and longjmp call are indeterminate.

这正是这里的情况:foo 具有自动存储持续时间并且在调用 setjmp 的函数中是本地的,它不是 volatile 限定的,并且它在setjmplongjmp.

代码的作者可能认为将 &foo 传递给 lib_var 将确保它是在内存中分配的,而不是在寄存器中,因为编译器无法知道是否lib_var 会修改 foo,它无法将值 NULL 常量传播到 else 块中。但正如您所说,LTO 或其他各种优化可能会打破这一点。作者可能只打算支持他们知道没有做任何此类事情的特定编译器,或者提供了一些超出标准规定的更强有力的保证,但也有可能他们只是感到困惑。

正确的解决方法是将 foo 声明为 volatile,即 struct Foo * volatile foo = NULL;.