45-超越 goto 的跳转 longjmp
来源:互联网 发布:阿里云dns教程 编辑:程序博客网 时间:2024/06/05 17:20
不明所以的同学可能觉得本篇和信号这一专题关系不大,实际上,本篇是为 sigsetjmp 和 siglongjmp 函数作铺垫。但是在这讲这两函数前,先学习更简单的 setjmp 函数和 longjmp 函数。
1. 回忆 goto
回忆 C 语言中的 goto 语句,它所起到的作用就直接从一条语句跳转到另一条语句。这种程序往往破坏了程序的结构,所以专家们都不提倡使用 goto 语句,不过这个我们并不关心,讲 goto 是为了引出 longjmp 函数。
大家都知道 goto 语句只能在函数内跳转,并不能跨越函数进行跳转,像下面这样。
void func() {hello: printf("hello world\n"); goto hello;}
跨越函数?不能,下面这种用法不可行。
void func1() {hello: printf("hello world\n"); func2();}void func2() { goto hello;}
不过,脑洞大开的程序员们设计了一个称之为 longjmp 的函数,它可以帮我们搞定这种 nb 的跳转。
2. longjmp
goto 语句有与之配套的一个标号,longjmp 也不例外,只不过 longjmp 配套的标号仍然是一个函数——setjmp.
如果修改前面的程序,大概是这样的:
jmp_buf hello;void func1() { setjmp(hello); printf("hello world\n"); func2();}void func2() { longjmp(hello);}
不要试图编译上面的程序,这已经被简化了。
3. 牛刀小试
先把上面的代码稍微修改修改就可以编译运行了:
- 代码
// longjmp.c#include <unistd.h>#include <setjmp.h>#include <stdio.h>jmp_buf hello; // 设置标号void func2() { longjmp(hello, 1); }void func1() { setjmp(hello); printf("hello world\n"); sleep(2); // 防止刷屏了 func2(); // 准备跳转}int main() { func1(); return 0;}
- 编译和运行
$ gcc longjmp.c -o longjmp$ ./longjmp
接下来,会在屏幕每 2 秒打印一个 hello world.
hello worldhello worldhello worldhello world...
4. 为什么会这样?
我知道你心中有一万头草泥马奔腾而过,不过,搞懂原理后,写出这样的函数对你来说简直就是渣。原理有点复杂,请参考下一篇博文(勿抛砖)。
5. 函数原型
5.1 setjmp
int setjmp(jmp_buf env)
当第一次程序显式调用 setjmp 时,它的返回值是 0. 此后通过 longjmp 跳转到 setjmp 这个位置时,setjmp 的返回值是 longjmp 函数的第二个参数的值。
setjmp 的参数 env 必须是一个全局变量,它用来保存当前程序运行环境(一系列寄存器及栈帧里的关键值)。此后 longjmp 需要依据此 env 来跳转到 setjmp 的位置。
实际上 jmp_buf 是一个固定大小的数组(比如大小为 16 ?)。
5.2 longjmp
void longjmp(jmp_buf env, int val);
longjmp 第一个参数就是通过 setjmp 函数初始化后的值,第二个参数将通过 setjmp 返回值返回。
5.3 最后一个例子
下面这段程序,从终端读数据。如果你输入 100,longjmp 传递参数 1 并跳转到 setjmp 的位置,同时 setjmp 会返回 1. 如果你输入 200,longjmp 传递参数 2 并跳转到 setjmp 的位置,同时 setjmp 将返回 2.
- 代码
// jmp.c#include <setjmp.h>#include <stdio.h>jmp_buf jmpbuf;void doSomething() { int n = 0; scanf("%d", &n); if (n == 100) { longjmp(jmpbuf, 1); } if (n == 200) { longjmp(jmpbuf, 2); }}int main() { int res = 0; if ((res = setjmp(jmpbuf)) != 0) { printf("hello! res = %d\n", res); } while(1) { doSomething(); }}
- 编译和运行
$ gcc jmp.c -o jmp$ ./jmp
输入 100 和 200 后的结果:
100 // 输入 100hello! res = 1200 // 输入 200hello! res = 2
6. 总结
- 学会 setjmp 和 longjmp 的用法
- 思考这是如何做到的?(下一篇讲解原理)
练习:请尝试自己实现一个 setjmp 和 longjmp 函数。(提示:1. 需要使用汇编;2. 由于 gcc 不支持编写 naked 函数,请使用 Visual Studio 编程器)。
- 45-超越 goto 的跳转 longjmp
- C语言的结构跳转 (goto / longjmp)
- C语言中的跳转goto&&setjmp&&longjmp sigsetjmp siglongjmp
- C语言中的跳转goto&&setjmp&&longjmp sigsetjmp siglongjmp
- c语言 goto 和 longjmp 的区别
- goto VS longjmp setjmp
- 详解C的异常处理机制(goto\setjmp longjmp)
- setjmp和longjmp的"非本地跳转"
- setjmp和longjmp的"非本地跳转"
- setjmp和longjmp goto应用
- 比goto跳转的更远的函数
- Linux函数之间的goto 跳转
- 全局跳转 setjmp longjmp
- linux 跳转 setjmp longjmp
- setjmp和longjmp(替代goto)
- 浅析goto语句以及longjmp函数
- 浅析C语言的非局部跳转:setjmp和longjmp
- setjmp()和longjmp()--c的非本地跳转
- 第一章练习
- Java 优雅的终止线程
- RGB颜色查询对照表
- make_ext4fs参数的权限说明
- VS2015如何添加解析
- 45-超越 goto 的跳转 longjmp
- Monkey测试命令及其说明附中止monkey测试方法
- IO 简单登录程序
- static
- JS/JQ 宽高的理解和应用--慕课
- [Leetcode] Maximum Subarray
- Centos日志及管理
- iOS 工作之余小总结(四)--(3) NSDate 时间类
- java变量默认值