在C中,goto语句是不能跨越函数的,而执行这样跳转功能的是函数setjmp和longjmp。这两个函数对于处理发生在深层嵌套函数调用中的出错情况是非常有用的。

setjmp和longjmp函数也称为非局部goto,非局部指的是,这不是由普通C语言goto语句在一个函数内实施的跳转,而是在栈上跳过若干调用帧,返回到当前函数调用路径上的某一函数中。

#include <setjmp.h>

int setjmp( jmp_buf env );
返回值:若直接调用则返回0,若从longjmp调用返回则返回非0值 void longjmp( jmp_buf env, int val );

 

图7-4  调用cmd_add后的各个栈帧(函数调用关系为:main先调用do_line,do_line又调用cmd_add)

在希望返回到的位置调用setjmp,假定此位置在main函数中,因为我们是直接调用该函数,所以其返回值为0。setjmp参数env的类型是一个特殊类型jmp_buf。这一数据类型是某种形式的数组,其中存放:在调用longjmp时能用来恢复栈状态的所有信息。因为需要在另一个函数中引用env变量,所以规范的处理方式是将env变量定义为全局变量。

当检查到一个错误时,例如在cmd_add函数中,则以两个参数调用longjmp函数。第一个就是在调用setjmp时所用的env;第二个参数是具有非0值的val,它将成为从setjmp处返回的值。使用第二个参数的原因是对于一个setjmp可以有多个longjmp。例如,可以在cmd_add中以val为1调用longjmp,也可在do_line中以val为2调用longjmp。在setjmp的返回值就会是1或2,通过测试返回值就可以判断造成返回的longjmp是在cmd_add还是在do_line中。

我们假定在main中调用了setjmp(jmpbuffer),在cmd_add中调用了longjmp(jmpbuffer, 1)为例进行后续说明。

...
#include <setjmp.h>
...
jmp_buf jmpbuffer; int
main(void)
{
...
if( setjmp(jmpbuffer) != 0 )
printf("error");
...
} ...
void
cmd_add(void)
{
...
if( ... ) /* an error has occurred */
longjmp( jmpbuffer, 1 );
...
}

执行main时,调用setjmp,它将所需的信息记入变量jmpbuffer中并返回0。然后调用do_line,它又调用cmd_add,假定在其中检查到一个错误。在cmd_add中调用longjmp之前,栈的形式如图7-4所示。但是longjmp使栈反绕(rewind to)到执行main函数时的情况,也就是抛弃了cmd_add和do_line的栈帧(见图7-5)。调用longjmp造成main中setjmp的返回,但是,这一次的返回值是1(longjmp的第二个参数)。

                   图7-5 调用longjmp后的栈帧(f1代表do_line、f2代表cmd_add)

1、自动、寄存器和易失变量

调用longjmp时,大多数实现并不回滚自动变量和寄存器变量的值,而所有标准则说它们的值是不确定的。如果你有一个自动变量,而又不想使其值回滚,则可定义其为具有volatile的属性。声明为全局或静态变量的值在执行longjmp时保持不变。

程序清单7-6 longjmp对各类变量的影响

[root@localhost apue]# cat prog7-6.c
#include "apue.h"
#include <setjmp.h> static void f1(int, int, int, int);
static void f2(void); static jmp_buf jmpbuffer;
static int globval; int
main(void)
{
int autoval;
register int regival;
volatile int volaval;
static int statval; globval = 1;
autoval = 2;
regival = 3;
volaval = 4;
statval = 5; if(setjmp(jmpbuffer) != 0)
{
printf("after longjmp:\n");
printf("globval = %d, autoval = %d, regival = %d,"
"volaval = %d, statval = %d\n", /* ISO C的字符串连接功能 */
globval, autoval, regival, volaval, statval);
exit(0);
}
/*
* Change variables after setjmp, but before longjmp.
*/
globval = 95;
autoval = 96;
regival = 97;
volaval = 98;
statval = 99; f1(autoval, regival, volaval, statval); /* never returns */
exit(0);
} static void
f1(int i, int j, int k, int l)
{
printf("in f1():\n");
printf("globval = %d, autoval = %d, regival = %d,"
"volaval = %d, statval = %d\n", globval, i, j, k, l);
f2();
} static void
f2(void)
{
longjmp(jmpbuffer, 1);
}

如果以不带优化和带优化选项对此程序分别进行编译,然后运行它们,则得到的结果是不同的:

[root@localhost apue]# cc -o prog7-6 prog7-6.c        不进行任何优化的编译
[root@localhost apue]# ./prog7-6
in f1():
globval = 95, autoval = 96, regival = 97,volaval = 98, statval = 99
after longjmp:
globval = 95, autoval = 96, regival = 97,volaval = 98, statval = 99
[root@localhost apue]# cc -o prog7-6 -O prog7-6.c 进行全部优化的编译
[root@localhost apue]# ./prog7-6
in f1():
globval = 95, autoval = 96, regival = 97,volaval = 98, statval = 99
after longjmp:
globval = 95, autoval = 2, regival = 3,volaval = 98, statval = 99

注意,全局、静态和易失变量不受优化的影响,在调用longjmp后,它们的值是最近所呈现的值。

某个系统的setjmp(3)手册页上说明,存放在存储器中的变量将具有longjmp时的值,而在CPU和浮点寄存器中的变量则恢复为调用setjmp时的值。

不进行优化时,所有这5个变量都存放在存储器中(亦即忽略了对regival变量的register存储类说明)。而进行了优化后,autoval和regival都存放在寄存器中(即使autoval并未声明为register),volatile变量则仍存放在存储器中。

通过这一实例我们可以理解到,如果要编写一个使用非局部跳转的可移植程序,则必须使用volatile属性。

2、自动变量的潜在问题

基本规则是声明自动变量的函数已经返回后,不能再引用这些自动变量。

本篇博文内容摘自《UNIX环境高级编程》(第二版),仅作个人学习记录所用。关于本书可参考:http://www.apuebook.com/

进程环境之setjmp和longjmp函数的更多相关文章

  1. 【转载】setjmp和longjmp函数使用详解

    [说明]本文上半部分转载自 wykwdy007 的转载文章 http://blog.csdn.net/wykwdy007/article/details/6535322 --------------- ...

  2. setjmp和longjmp函数使用详解

    源地址:http://blog.csdn.net/zhuanshenweiliu/article/details/41961975 非局部跳转语句---setjmp和longjmp函数.非局部指的是, ...

  3. setjmp()和longjmp()函数

    之前我们讲到了过程活动记录(AR),那么如何来操纵AR呢,一个可能的方法是,根据局部变量的地址进行推算,例如对于上面的a函数,执行a函数时的当前AR地址就是参数i的地址偏移8个字节,也就是 ((cha ...

  4. 学习之"setjmp和longjmp函数"

    Linux学习之"setjmp和longjmp函数"   转贴,原文地址:http://www.cnblogs.com/lq0729/archive/2011/10/23/2222 ...

  5. linux系统编程:setjmp和longjmp函数用法

    #include <stdio.h> #include <setjmp.h> //jmp_buf:数组,保存栈信息即运行环境 jmp_buf buf; double Divid ...

  6. setjmp和longjmp函数

    关于setjmp函数和longjmp函数有话要说,是UNIX高级环境变成看到了10.10信号那章用到了,研究一下,这里作为补充. setjmp(jmp_buf env_buf) 函数可以将当前的运行环 ...

  7. 进程环境之getrlimit和setrlimit函数

    每个进程都有一组资源限制,其中一些可以用getrlimit和setrlimit函数查询和更改. #include <sys/resource.h> int getrlimit( int r ...

  8. Linux-进程描述(5)之进程环境

    main函数和启动例程 当内核使用一个exec函数执行C程序时,在调用main函数之前先调用一个特殊的启动例程,可执行程序将此例程指定为程序的起始地址.启动例程从内核获取命令行参数和环境变量,然后为调 ...

  9. apue学习笔记(第七章 进程环境)

    本章将了解进程的环境. main函数 C程序总是从main函数开始执行,main函数的原型是: int main(int argc,char *argv[]); 其中,argc是命令行参数的数目,ar ...

随机推荐

  1. 【大话QT之十】实现FTP断点续传(需要设置ftp服务器为“PASV”被动接收方式)

    应用需求: 网盘开发工作逐步进入各部分的整合阶段,当用户在客户端修改或新增加一个文件时,该文件要同步上传到服务器端对应的用户目录下,因此针对数据传输(即:上传.下载)这一块现在既定了三种传输方式,即: ...

  2. laravel route路由,视图和response和filter

    Laravel充分利用PHP 5.3的特性,使路由变得简单并富于表达性.这使得从构建API到完整的web应用都变得尽可能容易.路由的实现代码在 application/routes.php 文件. 和 ...

  3. Ember.js demo7

    <!DOCTYPE html> <html> <head> <script src="http://code.jquery.com/jquery-1 ...

  4. 【PythonChallenge】Level 5

    题目主要找发声类似于Peak Hell的Python模块,查了一下手册pickle已经是最像的了.看了一下源代码,发现panner.p.如同发现了新大陆,拷贝内容.使用pickle解答.答案为chan ...

  5. error opening registry key software/javasoft/java runtime environment

    些问题是由于多个版本java相互冲突,查看环境变量,删除其中一个即可.1.把Path里的%JAVA_HOME%\bin;放到最前面就可以,解决了给分! 2.是因为文件夹“%SystemRoot%\sy ...

  6. write & read a sequence file(基于全新2.2.0API)

    write & read a sequence file write & read a sequence file import java.io.IOException; import ...

  7. Android学习之路——简易版微信为例(三)

    最近好久没有更新博文,一则是因为公司最近比较忙,另外自己在Android学习过程和简易版微信的开发过程中碰到了一些绊脚石,所以最近一直在学习充电中.下面来列举一下自己所走过的弯路: (1)本来打算前端 ...

  8. PL/SQL连接查询数据报错时Dynamic Performance Tables not accessible

    一.产生该提示原因plsql dev在用户运行过程中,要收集用户统计信息,但是由于你现在登录的用户没有访问v$session,v$sesstat and v$statname视图的权限,所以不能收集当 ...

  9. Oracle.ManagedDataAccessDTC.dll 使用

    ODP.NET, Managed Driver Setup This section explains the setup and configuration steps required for u ...

  10. linux下的rbenv和rails安裝

    今天是周一,我到新公司的第14天,今天继续linux下ruby和rails环境变量的配置. 首先碰到的问题是 主要看ubuntu下安装rbenv,ruby,rails开发环境, http://blog ...