繁体   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