簡體   English   中英

setjmp / longjmp 沒有跳到我認為應該跳的地方

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

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM