英文:
Confusing about things restored with longjmp?
问题
在这里提到:https://stackoverflow.com/a/58498330 当使用 setjmp
和 longjmp
,并返回到 setjmp
的作用域时,所有可访问的对象的值都与调用函数 longjmp
时相同。
我对此感到困惑,因为 longjmp
把我们带到程序中完全不同的地方,在我们调用 longjmp
时的局部变量毫无意义。所以当变量都不同的时候,它们怎么可能是“相同”的呢?
英文:
As mentioned here: https://stackoverflow.com/a/58498330 when using setjmp
and longjmp
, and returning to the scope of setjmp
, all accessible object have the same value as when the function longjmp
is called.
I am confused about this, because longjmp
takes us to a completely different place in the program, where the local variables when we call longjmp
make zero sense. So how can the value be "the same" when the variables are all different?
答案1
得分: 2
关键词在于 accessible。通过调用 longjmp
恢复的栈部分上的局部变量将不再存在,因此不再可访问。就此而言,故事结束。
值得指出的是,这些变量的析构函数(如果有的话)将不会被调用1。因此,不要在 C++ 代码中使用 longjmp
。相反,应使用 C++ 异常处理,以受控方式恢复栈。
1我认为 MSVC 实际上会调用它们,但如果您希望代码具有可移植性,不应依赖于此。
英文:
The key word here is accessible. Local variables on the part of the stack that is unwound by calling longjmp
cease to exist and are therefore no longer accessible. End of story.
It's worth pointing out that the destructors of those variables (if any) won't be called<sup>1</sup>. For this reason, don't use longjmp
in C++ code. Use C++ exception handling instead, which unwinds the stack in a controlled way.
<sup>1</sup>I think MSVC does actually call them, but this is not something you should rely on if you want your code to be portable.
答案2
得分: 2
以下是您要翻译的内容:
有关“可访问”变量被还原和未更改的误解。
引用中的短语是:
在返回到
setjmp
范围时,所有可访问的对象、浮点状态标志和抽象机器的其他组件都具有与执行std::longjmp
时相同的值,但setjmp
范围中的非易失性本地变量的值是不确定的,如果它们自setjmp
调用以来已更改,则为不确定。
当使用先前由setjmp
调用设置的jmp_buf
并且其调用帧仍然在调用堆栈中处于活动状态时,调用longjmp
时,会从jmp_buf
中保存的数据中恢复一部分处理器状态,如一些寄存器和标志,并将控制转移到setjmp
调用的返回地址,丢弃所有中间帧,而不调用任何析构函数或最终代码。唯一受影响的对象是setjmp
范围内的非易失性本地变量,它们可能已从jmp_buf
数据中还原,也可能没有,因此具有不确定的值。
这是一个示例:
#include <stdio.h>
#include <setjmp.h>
jmp_buf buf;
int v1;
volatile int v123 = 123, v456 = 456;
void willjmp(void) {
longjmp(buf, 42);
}
int main(void) {
volatile int v2;
int val, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12;
// 初始值
v1 = v123; v2 = v123; v3 = v123; v4 = v123;
v5 = v123; v6 = v123; v7 = v123; v8 = v123;
v9 = v123; v10 = v123; v11 = v123; v12 = v123;
if ((val = setjmp(buf)) != 0) {
printf("\nafter longjmp:\n");
printf("val=%d\n", val); // 打印 val=42
printf("v1=%d\n", v1); // 打印 v1=456
printf("v2=%d\n", v2); // 打印 v2=456
// 非易失性本地变量具有不确定的值
printf("v3..v12=%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
v3, v4, v5, v6, v7, v8, v9, v10, v11, v12);
} else {
printf("初始值:\n");
printf("val=%d\n", val); // 打印 val=42
printf("v1=%d\n", v1); // 打印 v1=123
printf("v2=%d\n", v2); // 打印 v2=123
// 打印 v3..v12=123,123,123,123,123,123,123,123,123,123
printf("v3..v12=%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
v3, v4, v5, v6, v7, v8, v9, v10, v11, v12);
v1 = v456; v2 = v456; v3 = v456; v4 = v456;
v5 = v456; v6 = v456; v7 = v456; v8 = v456;
v9 = v456; v10 = v456; v11 = v456; v12 = v456;
printf("\nbefore longjmp:\n");
printf("v1=%d\n", v1); // 打印 v1=456
printf("v2=%d\n", v2); // 打印 v2=456
// 打印 v3..v12=456,456,456,456,456,456,456,456,456,456
printf("v3..v12=%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
v3, v4, v5, v6, v7, v8, v9, v10, v11, v12);
willjmp();
}
return 0;
}
输出:
初始值:
val=0
v1=123
v2=123
v3..v12=123,123,123,123,123,123,123,123,123,123
before longjmp:
v1=456
v2=456
v3..v12=456,456,456,456,456,456,456,456,456,456
after longjmp:
val=42
v1=456
v2=456
v3..v12=123,123,123,123,123,123,123,123,123,123
根据处理器架构、编译器实现和配置,特别是优化标志,v3
到v16
的输出可能是456
或123
,或者甚至可能是其他内容。上面的输出是在我的M2笔记本上启用了优化的情况下生成的。如果禁用优化,最后一行将变为v3..v12=456,456,456,456,456,456,456,456,456,456
。
正如可以在Goldbolt的Compiler Explorer上看到的,不同的编译器产生不同的输出,例如gcc -O
输出v3..v12=123,123,123,123,123,123,456,456,456,456
。
英文:
There is a misunderstanding about what accessible variables are restored and unchanged.
The phrase in reference is:
> Upon return to the scope of setjmp
, all accessible objects, floating-point status flags, and other components of the abstract machine have the same values as they had when std::longjmp
was executed, except for the non-volatile local variables in setjmp
's scope, whose values are indeterminate if they have been changed since the setjmp
invocation.
When longjmp
is called with a jmp_buf
previously set by a call to setjmp
whose calling frame is still active in the call stack, part of processor state such as some of the registers and flags are restored from data saved in the jmp_buf
and control is transferred back to the return address of the setjmp
call, discarding all intermediary frames without calling any destructors or finalisation code. The only objects that are affected are non-volatile local variables in the setjmp
scope, which may have been restored from the jmp_buf
data or not, and thus have indeterminate values.
Here is an example:
#include <stdio.h>
#include <setjmp.h>
jmp_buf buf;
int v1;
volatile int v123 = 123, v456 = 456;
void willjmp(void) {
longjmp(buf, 42);
}
int main(void) {
volatile int v2;
int val, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12;
// initial values
v1 = v123; v2 = v123; v3 = v123; v4 = v123;
v5 = v123; v6 = v123; v7 = v123; v8 = v123;
v9 = v123; v10 = v123; v11 = v123; v12 = v123;
if ((val = setjmp(buf)) != 0) {
printf("\nafter longjmp:\n");
printf("val=%d\n", val); // prints val=42
printf("v1=%d\n", v1); // prints v1=456
printf("v2=%d\n", v2); // prints v2=456
// non volatile local variables have indeterminate values
printf("v3..v12=%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
v3, v4, v5, v6, v7, v8, v9, v10, v11, v12);
} else {
printf("initial values:\n");
printf("val=%d\n", val); // prints val=42
printf("v1=%d\n", v1); // prints v1=123
printf("v2=%d\n", v2); // prints v2=123
// prints v3..v12=123,123,123,123,123,123,123,123,123,123
printf("v3..v12=%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
v3, v4, v5, v6, v7, v8, v9, v10, v11, v12);
v1 = v456; v2 = v456; v3 = v456; v4 = v456;
v5 = v456; v6 = v456; v7 = v456; v8 = v456;
v9 = v456; v10 = v456; v11 = v456; v12 = v456;
printf("\nbefore longjmp:\n");
printf("v1=%d\n", v1); // prints v1=456
printf("v2=%d\n", v2); // prints v2=456
// prints v3..v12=456,456,456,456,456,456,456,456,456,456
printf("v3..v12=%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
v3, v4, v5, v6, v7, v8, v9, v10, v11, v12);
willjmp();
}
return 0;
}
Output:
initial values:
val=0
v1=123
v2=123
v3..v12=123,123,123,123,123,123,123,123,123,123
before longjmp:
v1=456
v2=456
v3..v12=456,456,456,456,456,456,456,456,456,456
after longjmp:
val=42
v1=456
v2=456
v3..v12=123,123,123,123,123,123,123,123,123,123
Depending on the processor architecture, the compiler implementation and configuration, especially optimisation flags, the output for v3
through v16
could be 456
or 123
or possibly even something else. The output above was produced on my M2 laptop with optimisations enabled. With optimisations disabled, the last line becomes v3..v12=456,456,456,456,456,456,456,456,456,456
.
As can be seen on Goldbolt's Compiler Explorer, different compilers produce different output, for example gcc -O
outputs v3..v12=123,123,123,123,123,123,456,456,456,456
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论