关于setjmp和longjmp的使用

解决这种问题的方法就是使用非局部跳转——setjmp和longjmp函数。非局部表示这不是在一个函数内的普通的C语言goto语句,而是在栈上跳过若干调用帧,返回到当前函数调用路径上的一个函数中。
#include <setjmp.h>
int setjmp(jmp_buef nv);
返回:若直接调用则为0,若从longjmp返回则为非0
void longjmp(jmp_buef nv , int val);
在希望返回到的位置调用setjmp,在本例中,此位置在main函数中。因为我们直接调用该函数,所以其返回值为0。setjmp的参数env是一个特殊类型jmpbuf。这一数据类型是某种形式的数组,其中存放在调用longjmp时能用来恢复栈状态的所有信息。一般,env变量是个全局变量,因为需从另一个函数中引用它。

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


#include "ourhdr.h"
#include <setjmp.h>
 
#define TOK_ADD    5
 
jmp_buf jmpbuffer;
 
char    *tok_ptr;              
 
void    do_line(char *);
void    cmd_add(void);
int             get_token(void);
 
int
main(void)
{
        char    line[MAXLINE];
 
        printf("setjmp\n");
        if (setjmp(jmpbuffer) != 0)
                printf("error\n");
        printf("setjmp 2\n");
        while (fgets(line, MAXLINE, stdin) != NULL)
                do_line(line);
        exit(0);
}
 
void
do_line(char *ptr)             
{
        int             cmd;
 
        tok_ptr = ptr;
        while ((cmd = get_token()) > 0) {
                switch (cmd) { 
                case TOK_ADD:
                                cmd_add();
                                break;
                }
        }
        printf("cmd=%d\n", cmd);
}
 
 
 
void
cmd_add(void)
{
        int             token;
 
        token = get_token();
        printf("token=%d\n", token);
        //if (token < 0)               
                longjmp(jmpbuffer, 1);
       
}
 
int
get_token(void)
{
       
        return TOK_ADD;
}
 

程序执行结果:
setjmp
setjmp 2
1
token=5
error
setjmp 2
2
token=5
error

------------------------------------------------------------------------
setjmp 2
3
token=5
error
setjmp 2
4
token=5
error
setjmp 2
5
token=5
error
setjmp 2
6
token=5
error
setjmp 2
7
token=5
error
setjmp 2
8
token=5
error
setjmp 2
9
token=5
error
setjmp 2

通过结果可以看到:
在执行到longjmp时,程序没有继续执行了,而是直接从setjmp处执行,因为setjmp的返回值是longjmp的第二个参数,所以setjmp返回1,这样if条件成立,就答应出error,然后程序继续执行才会打印出setjmp2。

可见setjmp和longjmp结合使用是可以实现长距离跳转,即非函数之内的局部跳转。

下一个问题是:“在main函数中,自动变量和寄存器变量的状态如何?”当longjmp返回到main函数时,这些变量的值是否能恢复到以前调用setjmp时的值(即滚回原先值),或者这些变量的值保持为调用do_line时的值(do_line调用cmd_add,cmd_add又调用longjmp)?不幸的是,对此问题的回答是“看情况”。大多数实现并不滚回这些自动变量和寄存器变量的值,而所有标准则说它们的值是不确定的。如果你有一个自动变量,而又不想使其值滚回,则可定义其为具有volatile属性。说明为全局和静态变量的值在执行longjmp时保持不变。通过这一例子要理解的是,如果要编写一个使用非局部跳转的可移植程序,则必须使用volatile属性。

你可能感兴趣的:(c,cmd,null,语言,token)