概念
goto可能会导致程序逻辑的混乱,并且非常容易导致程序栈帧的混乱,造成不可预知的后果。
setjump和longjump用于代替goto。由于操作系统调用会有很“深”的函数调用,当某些操作完成后,系统调用函数想要快速返回用户层函数而不是一层层地返回,所以这里经常用setjmp/longjmp。
另外笔者实践发现,某些时候递归函数使用setump/longjmp也能有很神奇的用处。
- 使用setjmp/longjmp宏要包含setjmp.h
1
| int setjmp(jmp_buf env);
|
- env是jmp_buf类型的变量,用于保存setjmp时的环境
- 第一次调用setjmp必返回0,之后依据longjmp返回
1
| void longjmp(jmp_buf env,int ret_val);
|
(void是我猜的)
- env 之前setjmp保存的环境,跳转到保存的那一行
- ret_val 跳转到setjmp时返回值
例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #include <stdio.h> #include <setjmp.h>
jmp_buf buf; void second(){ printf("second\n"); longjmp(buf,1); } void first(){ second(); printf("first\n"); } int main() { if(!setjmp(buf)){ first(); }else{ printf("main\n"); } }
|
输出
底层实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| buf: .zero 200 .LC0: .string "second" second(): push rbp mov rbp, rsp mov edi, OFFSET FLAT:.LC0 call puts mov esi, 1 mov edi, OFFSET FLAT:buf call longjmp .LC1: .string "first" first(): push rbp mov rbp, rsp call second() mov edi, OFFSET FLAT:.LC1 call puts nop pop rbp ret .LC2: .string "main" main: push rbp mov rbp, rsp mov edi, OFFSET FLAT:buf call _setjmp test eax, eax sete al test al, al je .L5 call first() jmp .L6 .L5: mov edi, OFFSET FLAT:.LC2 call puts .L6: mov eax, 0 pop rbp ret
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| typedef struct _JUMP_BUFFER { unsigned __int64 Frame; unsigned __int64 Rbx; unsigned __int64 Rsp; unsigned __int64 Rbp; unsigned __int64 Rsi; unsigned __int64 Rdi; unsigned __int64 R12; unsigned __int64 R13; unsigned __int64 R14; unsigned __int64 R15; unsigned __int64 Rip; unsigned long MxCsr; unsigned short FpCsr; unsigned short Spare;
SETJMP_FLOAT128 Xmm6; SETJMP_FLOAT128 Xmm7; SETJMP_FLOAT128 Xmm8; SETJMP_FLOAT128 Xmm9; SETJMP_FLOAT128 Xmm10; SETJMP_FLOAT128 Xmm11; SETJMP_FLOAT128 Xmm12; SETJMP_FLOAT128 Xmm13; SETJMP_FLOAT128 Xmm14; SETJMP_FLOAT128 Xmm15; } _JUMP_BUFFER;
|
可以看到buffer内保存了所有寄存器和相关信息
参考资料
[1] C 库宏 - setjmp()