[英]setjmp / longjmp does not jump where I think it should
我想了解 setjmp / longjmp 是如何工作的,所以我创建了一个示例程序,其中routineA 打印偶数,routineB 打印奇数,它们使用 longjmp 相互跳转:
#include <setjmp.h>
#include <stdio.h>
#define COUNTER_BEGIN 0
#define COUNTER_END 6
void routineA( jmp_buf* pEnvA, jmp_buf* pEnvB );
void routineB( jmp_buf* pEnvA, jmp_buf* pEnvB );
int main() {
const char message[] = "main [ &envA=0x^%016lx &envB=0x^%016lx ] -- %s\n";
jmp_buf envA;
jmp_buf envB;
fprintf( stdout, message, &envA, &envB,
"Started; Before calling routineA" );
routineA( &envA, &envB );
fprintf( stdout, message, &envA, &envB,
"After routineA returned; Exiting" );
return 0;
}
/* Prints even numbers from COUNTER_BEGIN to COUNTER_END */
void routineA( jmp_buf* pEnvA, jmp_buf* pEnvB ) {
const char* message = "routineA: [ i=%d, sjr=%d ] -- %s\n";
static int i;
static int sjr;
static jmp_buf *s_pEnvA;
static jmp_buf *s_pEnvB;
s_pEnvA = pEnvA;
s_pEnvB = pEnvB;
fprintf( stdout, message, i, sjr, "After saving statics; Before starting the for loop" );
for( i = COUNTER_BEGIN; i < COUNTER_END; i++ ) {
if( i % 2 == COUNTER_BEGIN + 0 ) {
fprintf( stdout, message, i, sjr, "Inside for and if; Before setjmp" );
sjr = setjmp( *s_pEnvA ); /* Added */
if( sjr == 0 ) { /* Added */
/* if( ( sjr = setjmp( *s_pEnvA ) ) == 0 ) { */
fprintf( stdout, message, i, sjr, "Got to this line directly - go to routineB somehow" );
if( i == 0 ) {
fprintf( stdout, message, i, sjr, "This is the first iteration - call routineB as function" );
routineB( s_pEnvA, s_pEnvB );
} else {
fprintf( stdout, message, i, sjr, "This is not the first iteration - longjmp to routineB" );
longjmp( *s_pEnvB, 12 );
}
} else {
fprintf( stdout, message, i, sjr, "Got to this line via longjmp (in this program it must be from routineB) - continue" );
; /* Just continue */
}
}
}
fprintf( stdout, message, i, sjr, "After the for loop, Before leaving the function" );
}
/* Prints odd numbers from COUNTER_BEGIN to COUNTER_END */
void routineB( jmp_buf* pEnvA, jmp_buf* pEnvB ) {
const char* message = "routineB: [ i=%d, sjr=%d ] -- %s\n";
static int i;
static int sjr;
static jmp_buf *s_pEnvA;
static jmp_buf *s_pEnvB;
s_pEnvA = pEnvA;
s_pEnvB = pEnvB;
fprintf( stdout, message, i, sjr, "After saving statics; Before starting the for loop" );
for( i = COUNTER_BEGIN; i < COUNTER_END; i++ ) {
if( i % 2 == 1 ) {
fprintf( stdout, message, i, sjr, "Inside for and if; Before setjmp" );
sjr = setjmp( *s_pEnvB ); /* Added */
if( sjr == 0 ) { /* Added */
/* if( ( sjr = setjmp( *s_pEnvB ) ) == 0 ) { */
fprintf( stdout, message, i, sjr, "Got to this line directly - longjmp to routineA" );
longjmp( *s_pEnvA, 21 );
} else {
fprintf( stdout, message, i, sjr, "Got to this line via longjmp (in this program it must be from routineA) - continue" );
; /* Just continue */
}
}
}
fprintf( stdout, message, i, sjr, "After the for loop, Before leaving the function" );
}
我用 gcc 编译它并收到以下 output (插入行号以供以后参考):
01 main [ &envA=0x^00007ffce81b0280 &envB=0x^00007ffce81b01b0 ] -- Started; Before calling routineA
02 routineA: [ i=0, sjr=0 ] -- After saving statics; Before starting the for loop
03 routineA: [ i=0, sjr=0 ] -- Inside for and if; Before setjmp
04 routineA: [ i=0, sjr=0 ] -- Got to this line directly - go to routineB somehow
05 routineA: [ i=0, sjr=0 ] -- This is the first iteration - call routineB as function
06 routineB: [ i=0, sjr=0 ] -- After saving statics; Before starting the for loop
07 routineB: [ i=1, sjr=0 ] -- Inside for and if; Before setjmp
08 routineB: [ i=1, sjr=0 ] -- Got to this line directly - longjmp to routineA
09 routineA: [ i=0, sjr=21 ] -- Got to this line via longjmp (in this program it must be from routineB) - continue
10 routineA: [ i=2, sjr=21 ] -- Inside for and if; Before setjmp
11 routineA: [ i=2, sjr=0 ] -- Got to this line directly - go to routineB somehow
12 routineA: [ i=2, sjr=0 ] -- This is not the first iteration - longjmp to routineB
13 routineA: [ i=2, sjr=21 ] -- Got to this line via longjmp (in this program it must be from routineB) - continue
14 routineA: [ i=4, sjr=21 ] -- Inside for and if; Before setjmp
15 routineA: [ i=4, sjr=0 ] -- Got to this line directly - go to routineB somehow
16 routineA: [ i=4, sjr=0 ] -- This is not the first iteration - longjmp to routineB
17 routineA: [ i=4, sjr=21 ] -- Got to this line via longjmp (in this program it must be from routineB) - continue
18 routineA: [ i=6, sjr=21 ] -- After the for loop, Before leaving the function
19 main [ &envA=0x^00007ffce81b0280 &envB=0x^00007ffce81b01b0 ] -- After routineA returned; Exiting
output 直到第 12 行都可以: This is not the first iteration - longjmp to routineB
,但是,在此之后它应该 go 到routineB,并Got to this line via longjmp (in this program it must be from routineA) - continue
但它停留在routineA中,并说Got to this line via longjmp (in this program it must be from routineB) - continue
。
我的代码中有错误还是我误解了 setjmp / longjmp 的某些内容?
更新
根据 Eric Postpischil 的回答的第一部分修改了代码:我认为现在它适合最后一个要点,因为赋值语句是表达式语句。 我收到了相同的结果(除了不同的指针值)。 现在,我来看看第二部分。
首先,您对setjmp
的调用行为未由 C 标准定义,因为它们违反了 C 2018 7.13.1.1 4 和 5 中的约束:
setjmp
宏的调用应仅出现在以下上下文之一中:— 选择或迭代语句的整个控制表达式;
— 关系或相等运算符的一个操作数与另一个操作数 integer 常量表达式,结果表达式是选择或迭代语句的整个控制表达式;
— 一元的操作数
!
运算符,其结果表达式是选择或迭代语句的整个控制表达式; 或者— 表达式语句的整个表达式(可能强制转换为
void
)。如果调用出现在任何其他上下文中,则行为未定义。
例如,在if( ( sjr = setjmp( *s_pEnvA ) ) == 0 )
中, setjmp
调用不是整个控制表达式,它不是关系或相等运算符 ( <
, <=
, >
, >=
、 ==
或!=
),它不是!
的操作数 , 并不是一个表达式语句的全部表达式。
其次, longjmp
只能向上跳转调用堆栈,跳转到仍在执行的函数。 一旦对 function 的调用停止执行(当它返回时),您就不能跳回该调用。 您的代码将上下文保存在routineB
中,然后返回到routineA
,然后尝试跳转到保存的上下文。 但是 C 2018 7.13.2.1 2,关于longjmp
说:
... 如果包含
setjmp
宏调用的 function 在此期间终止了执行,... 行为未定义。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.