20.1 setjmp 和 longjmp 函数

20.1.1 函数介绍

#include <setjmp.h>
int setjmp(jmp_buf env);
  • 函数功能:设置非局部跳转的跳转点(设置跳转点)
  • 返回值:直接调用返回0,若从 longjmp 调用返回则返回0
  • 这个函数会被执行两次,一次是自己本身使用的时候返回0,另一次再调用 longjump 的时候,此函数再返回 longjmp 中的 val 值
#include <setjmp.h>
void longjmp(jmp_buf env, int val);
  • 函数功能:进行非局部跳转,val 为返回值(具体完成跳转,例如goto)
  • 参数:
    • @env:

      • 一个特殊类型 jmp_buf。这一数据类型是某种形式的数组,其中存放在调用 longjmp 时能用来恢复栈状态的所有信息。一般,env 变量是个全局变量,因为需从另一个函数中引用他。
  • C程序缺乏异常处理的语法,可使用非局部跳转处理C程序的异常
  • goto语句仅限于函数内部的跳转,而 longjmp 不限于

20.1.2 例子

  process_jmp.c

 #include <setjmp.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h> #define TOK_ADD 5
#define TOK_SUB 6 void do_line(char *line);
void cmd_add(void);
void cmd_sub(void);
int get_token(char *item);/* 获取分割字符 */ char *prompt = "cal:"; /* 命令行提示符 */
jmp_buf env;/* 跳转的 buf 结构 */ int main(void)
{
ssize_t size = strlen(prompt) * sizeof(char);
char buff[];
ssize_t len; /* 设置跳转点 */
/* setjmp 第一次执行成功返回0,调用 longjmp 后此处再返回 非0值 */
26 if(setjmp(env) < 0) {
27 perror("setjmp error");
28 exit(1);
29 }

write(STDOUT_FILENO, prompt, size);
while() {
len = read(STDIN_FILENO, buff, );
if(len < ) break; buff[len - ] = ;
do_line(buff);
write(STDOUT_FILENO, prompt, size);
} return ;
} void do_line(char *line)
{
int cmd = get_token(line); switch(cmd) {
case TOK_ADD:
cmd_add();
break;
case TOK_SUB:
cmd_sub();
break;
default:
fprintf(stderr, "error command\n");
} } void cmd_add(void)
{
int i = get_token(NULL);
int j = get_token(NULL);
printf("result is %d\n", i + j);
} void cmd_sub(void)
{
int i = get_token(NULL);
int j = get_token(NULL);
printf("result is %d\n", i - j);
} static int is_number(char *item)
{
int len = strlen(item);
int i; for(i = ; i < len; i++)
{
if(item[i] > '' || item[i] < '')
return ;
} return ;
} int get_token(char *line)
{
/*
* add 3 4
*/
char *item = strtok(line, " "); if(line != NULL) {
if(!strcmp("add", item)) return TOK_ADD;
if(!strcmp("sub", item)) return TOK_SUB;
} else {
if(is_number(item)) {
int i = atoi(item);
return i;
} else {
fprintf(stderr, "arg not number\n");
/* 如果输入的参数不正常,则让程序跳回到主函数执行下一次循环 */
/* 进行非局部跳转 */
longjmp(env, 1);// 跳转到 setjmp 处执行
}
}
}

  执行成功

  

  如果将红色部分注释掉,会发现打印 reasult is xx 数字,xx数字 是一个随机值,因为再 get_token 函数中,fprintf 后就没有做退出也没有做返回实际数字,那么函数运行完毕后,就会返回一个随机值来做加减运行,结果也就变为了一个随机值。

20.2 非局部跳转中,变量的使用

  编译器优化编译后:

  • 全局变量、静态变量和 volatile(易矢变量)

    • 不能恢复到原始值
  • 寄存器变量
    • 可以恢复到原始值  
  • 自动变量潜在问题  
    • 优化编译后可能会恢复
  • malloc 变量
    • 与编译器优化有关,有的编译器进行优化编译会改变,有的编译器不会,具体看编译器优化    

  longjmp_val.c

 #include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h> int g_val; jmp_buf env; /*
* g_val:全局变量
* s_val:静态变量
* a_val:自动变量,即局部变量
* r_val:寄存器变量
* m_val:通过 malloc 分配的变量
* v_val:易失变量
*/
void fun1(int g_val, int s_val, int a_val, int r_val, int m_val, int v_val); void fun2(); int main(void)
{
static int s_val;
int a_val;
register r_val;
int *m_val = (int *)malloc(sizeof(int));
volatile int v_val; g_val = ;
s_val = ;
a_val = ;
r_val = ;
*m_val = ;
v_val = ; int k = ; if((k = setjmp(env)) < ) {
perror("setjmp error");
exit();
} else if( k == ) {
printf("after longjmp\n");
printf("g_val %d, s_val %d, a_val %d r_val %d, m_val %d v_val %d\n",
g_val, s_val, a_val, r_val, *m_val, v_val);
exit();
} g_val = ;
s_val = ;
a_val = ;
r_val = ;
*m_val = ;
v_val = ; fun1(g_val, s_val, a_val, r_val, *m_val, v_val); return ;
} void fun1(int g_val, int s_val, int a_val, int r_val, int m_val, int v_val)
{
printf("before longjmp\n"); printf("g_val %d, s_val %d, a_val %d r_val %d, m_val %d v_val %d\n",
g_val, s_val, a_val, r_val, m_val, v_val); fun2();
} void fun2()
{
longjmp(env, );
}

  不进行优化编译后执行:

  

  

  优化编译后:

  

  

二十、Linux 进程与信号---非局部跳转的更多相关文章

  1. 三十二、Linux 进程与信号——不可靠信号

    32.1 不可靠信号问题 发生信号时关联动作被重置为默认设置 信号可能丢失 程序片段 在进入 sig_int 与再次调用 signal 之间发生的 SIGINT 信号将不会捕获 导致进程终止 以前版本 ...

  2. 二十二、Linux 进程与信号---进程创建

    22.1 fork 和 vfork 函数 22.1.1 函数说明 #include <unistd.h> #include <sys/types.h> pid_t fork( ...

  3. 二十二、Linux 进程与信号---进程创建(续)

    22.2 父子进程操作文件 文件操作由两种模式: IO 系统调用操作文件 标准C IO 操作文件 看代码: #include <unistd.h> #include <string. ...

  4. Unix系统编程()执行非局部跳转:setjmp和longjmp

    使用库函数setjmp和longjmp可执行非局部跳转(local goto). 术语"非局部(nonlocal)"是指跳转目标为当前执行函数之外的某个位置. C语言里面有个&qu ...

  5. 【转】浅析C语言的非局部跳转:setjmp和longjmp

    转自 http://www.cnblogs.com/lienhua34/archive/2012/04/22/2464859.html C语言中有一个goto语句,其可以结合标号实现函数内部的任意跳转 ...

  6. 三十、Linux 进程与信号——信号的概念及 signal 函数

    30.1 信号的基本概念 信号(signal)机制是Linux 系统中最为古老的进程之间的通信机制,解决进程在正常运行过程中被中断的问题,导致进程的处理流程会发生变化 信号是软件中断 信号是异步事件 ...

  7. 十八、Linux 进程与信号---进程介绍

    18.1 进程的概念 程序:程序(program)是存放再磁盘文件中的可执行文件 进程 程序的执行实例被称为进程(process) 一个程序的执行实例可能由多个 进程具有独立的权限和职责.如果系统中某 ...

  8. 三十四、Linux 进程与信号——信号特点、信号集和信号屏蔽函数

    34.1 信号特点 信号的发生是随机的,但信号在何种条件下发生是可预测的 进程杠开始启动时,所有信号的处理方式要么默认,要么忽略:忽略是 SIGUSR1 和 SIGUSR2 两个信号,其他都采取默认方 ...

  9. 二十三、Linux 进程与信号---进程链和进程扇、守护进程和孤儿进程以及僵尸进程

    23.1 进程链和进程扇 23.1.1 概念 进程链:一个父进程构建出一个子进程,子进程再构建出子子进程,子子进程构建出子子子进程.... 这种就为进程链 进程扇:一个父进程构建出多个子进程,子进程都 ...

随机推荐

  1. 51nod 1443 路径和树(最短路树)

    题目链接:路径和树 题意:给定无向带权连通图,求从u开始边权和最小的最短路树,输出最小边权和. 题解:构造出最短路树,把存留下来的边权全部加起来.(跑dijkstra的时候松弛加上$ < $变成 ...

  2. Chemical table CFR500 div2D(并查集)

    给定的一个n*m的区域内,给出一些点的坐标,这些点上有一个元素,如果在矩形的子矩形的三个点都有元素,那么第四个点的元素可以自己产生,其他的元素需要购买,问最少需要购买多少中元素才可以把这个区域给填满. ...

  3. 整车CAN网络介绍

    CAN(Controller Area Network)控制器局域网络,CAN网络在早期的整车应用中以BCM(车身控制器)为控制中心,主要是车身零部件(雨刮/大灯/车窗…),智能硬件较少,所以早期的正 ...

  4. ST算法(倍增)(用于解决RMQ)

    ST算法 在RMQ(区间最值问题)问题中,我了解到一个叫ST的算法,实质是二进制的倍增. ST算法能在O(nlogn)的时间预处理后,用O(1)的时间在线回答区间最值. f[i][j]表示从i位起的2 ...

  5. CodeForces - 589B(暴力)

    题目链接:http://codeforces.com/problemset/problem/589/B 题目大意:告诉你n 个矩形,知道矩形的长度和宽度(长和宽可以互换),每个矩形的长度可以剪掉一部分 ...

  6. 洛谷P2486 染色

    LCT的一种姿势. 题意:给定一棵树.每次把一条路径上的点染成一种颜色,求一条路径上有多少段颜色. 解: 首先可以很轻易的用树剖解决,只不过代码量让人望而却步... 有一种难以想象的LCT做法... ...

  7. key-event

    效果如下 代码如下: 首先看目录 //index.html <!DOCTYPE html> <html lang="zh-CN"> <head> ...

  8. c#反射(2)

    public void Test1() { string ClassName="Person"; string MethodName="Test1"; //得到 ...

  9. OpenRefine

    无论是现今的大数据还是企业内部的小数据,都存在一些普遍的问题,如数据格式不对需要转换,一个单元格内包含多个含义的内容,包含重复项等等,虽然我们也可以使用excel解决,但是excel天生有诸多限制,比 ...

  10. 「Python」6种python中执行shell命令方法

    用Python调用Shell命令有如下几种方式: 第一种: os.system("The command you want"). 这个调用相当直接,且是同步进行的,程序需要阻塞并等 ...