[英]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.