zoukankan      html  css  js  c++  java
  • 7.10 setjmp和longjmp函数

    7.10 setjmplongjmp

    C中,goto语句是不能跨越。而种跳功能的是函setjmplongjmp这两个数对生在很深的嵌套函数调用中的非常有用

    一下程序7 - 4的骨干部分。其主循从标1行,然后do_line理每一入行。然后get_token从该输入行中取下一个记号。一行中的第一个记号假定是某种形式的一命令,于是switch句就实现命令选择。我的程序只理一命令,此命令cmd_add

    程序7.4 进行命令处理的典型程序骨架

    #include <stdio.h>

    #include <stdlib.h>

    #include <setjmp.h>



    #define TOK_ADD 5

    #define MAXLINE 128



    void do_line(char *);

    void cmd_add(void);

    int get_token(void);



    int main(void)

    {

    char line[MAXLINE];



    while (fgets(line, MAXLINE, stdin) != NULL)

    do_line(line);

    exit(0);

    }



    char *tok_ptr; //global pointer for get_token



    void do_line(char *ptr) //process one line of input

    {

    int cmd;



    tok_ptr = ptr;

    while ((cmd = get_token()) > 0) {

    switch (cmd) {

    case TOK_ADD:

    cmd_add();

    break;

    }

    }

    }



    void cmd_add(void)

    {

    int token;



    token = get_token();

    //reset of processing for this command

    }



    int get_token(void)

    {

    //fetch next token from line pointer to by tok_ptr

    }


    程序7 - 4命令、确定命令的型、然后用相数处理每一命令这类程序中是非常典型的。7 - 4示了用了cmd_add之后的大致使用情

    动变量的存储单元在每栈桢数组linemain栈帧中,整型cmddo_line栈帧中,整型tokencmd_add栈帧中。

    如上所述,种形式的安排是非常典型的,但并不要求非如此不可。并不一定要向低地址方向充。某些系统对栈有提供特殊的硬件支持,此C实现可能要用接表实现栈帧

    编写如程序7 - 4这样的程序中遇到的一个问题是,如何理非致命性的错误。例如,若cmd_add数发现个错误,譬如个无效的,那么它可能先打印一消息,然后希望忽略入行的余下部分,返回main下一入行。用C言比较难做到(在本例中, cmd_add只比main两个层次,在有些程序中往往低五或更多次。)如果我不得不以查返回值的方法逐返回,那就会变得很麻

    决这问题的方法就是使用非局部goto—— setjmplongjmp。非局部表示不是在数内的普通的Cg o t o句,而是在上跳若干,返回到前函数调用路上的一

    #include <setjmp.h>

    int setjmp(jmp_buf env) ;

    返回:若直接则为0,若longjmp返回则为0

    void longjmp(jmp_buf env, int val) ;

    希望返回到的位置setjmp,在本例中,此位置在main中。因直接,所以其返回值0setjmp参数env是一特殊j m p _ b u f型是某种形式数组,其中存放在longjmp能用恢复栈状态的所有信息。一般, env量是全局,因另一中引用它

    当检查到一个错误时,例如在cmd_add中,两个参数调longjmp。第一是在setjmp所用的env;第二v a l,是0值,它成为从setjmp返回的值。使用第二个参数的原因是于一setjmp可以有多longjmp。例如,可以在cmd_add中以v a l1longjmp,也可在get_token中以v a l2longjmp。在main中,setjmp的返回值就12,通过测试返回值就可判cmd_addget_tokenlongjmp

    再回到程序例中,程序7 - 5出了修改后的maincmd_add(其他两个do_lineget_token更改)

    #include <stdio.h>

    #include <stdlib.h>

    #include <setjmp.h>



    #define TOK_ADD 5

    #define MAXLINE 128



    jmp_buf jmpbuffer;



    void do_line(char *);

    void cmd_add(void);

    int get_token(void);



    int main(void)

    {

    char line[MAXLINE];



    if (setjmp(jmpbuffer) != 0)

    printf("error");



    while (fgets(line, MAXLINE, stdin) != NULL)

    do_line(line);

    exit(0);

    }



    char *tok_ptr; //global pointer for get_token



    void do_line(char *ptr) //process one line of input

    {

    int cmd;



    tok_ptr = ptr;

    while ((cmd = get_token()) > 0) {

    switch (cmd) {

    case TOK_ADD:

    cmd_add();

    break;

    }

    }

    }



    void cmd_add(void)

    {

    int token;



    token = get_token();

    //reset of processing for this command



    if (token < 0) //an error has occurred

    longjmp(jmpbuffer, 1);

    //reset of processing for this command

    }



    int get_token(void)

    {

    //fetch next token from line pointer to by tok_ptr

    }


    mainsetjmp,它所需的信息j m p b u ff e r中并返回0。然后do_line,它又c m _ a d d,假定在其中检测到一个错误。在cmd_addlongjmp之前,的形式如7 - 4中所示。但是longjmp使main数时的情,也就是抛弃cmd_adddo_line栈帧longjmp造成mainsetjmp的返回,但是,一次的返回值是1 ( longjmp的第二个参数)

          那么此时的栈帧结构就是只有main的栈帧,其他函数的都已经被抛弃了。

    7.10.1 、寄存器和易失

    下一个问题是:“在main中,自动变量和寄存器量的状态如何?longjmp返回到main数时量的值是否能恢复到以前setjmp的值(即回原先值),或者量的值保持为调do_line的值( do_linecmd_addcmd_addlongjmp ) ?不幸的是,问题的回答是“看情”。大多数实现并不些自动变量和寄存器量的值,而所有则说的值是不确定的。如果你有一动变量,而又不想使其值回,可定具有volatile性。全局和静态变量的值在longjmp保持不

    下面我程序7 - 6明在longjmp后,自动变量、寄存器量和易失量的不同情。如果以不优化和优化此程序分别进编译,然后行它得到的果是不同的:

    $ cc testjmp.c 行任何优化的编译

    $ a . o u t

    in f1(): count = 97, val = 98, sum = 99

    after longjmp: count = 97, val = 98, sum = 99

    $ cc -O testjmp.c 行全部优化的编译

    $ a . o u t

    in f1(): count = 97, val = 98, sum = 99

    after longjmp: count = 2, val = 3, sum = 99

    注意,易失( s u m )不受优化的影,在longjmp之后的值,是它在f 1的值。在我所使用的setjmp ( 3 )册页明存放在存器中的具有longjmp的值,而在CPU和浮寄存器中的恢复为调setjmp的值。就是在行程序7 - 5察到的值。

    行优化,所有个变量都存放在存器中(亦即v a l的寄存器存储类被忽略)。而优化c o u n tv a l都存放在寄存器中(即使c o u n t并末register ) , volatile仍存放在存器中。通过这一例子要理解的是,如果要编写使用非局部跳的可移植程序,使volatile

    程序7-6 longjmp,寄存器和易失量的影

    #include <stdio.h>

    #include <stdlib.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", globval, autoval,

    regival, volaval, statval);

    exit(0);

    }

    //change varivables 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);

    }



     

    注意,全局、静态和易失变量不受优化的影响。在调用longjmp后,他们的值是最近所呈现的值。存放在存储器中的变量将具有longjmp时的值,而在CPU和浮点数寄存器中的变量则恢复为调用setjmp时的值。

          在程序中,某些printf的格式字符串可能不适宜安排在程序文本的一行中,我们没有将其分成多个printf调用,而是使用了ISO C的字符串连接功能,于是两个字符串序列:

    “string1” “string2”

    等价于

    string1string2

    7.10.2 自动变量的潜在问题

    前面已经说明了处理栈帧的一般方式,与此相关我们现在可以分析一下自动变量的一个潜在出错情况。基本规则是说明自动变量的函数已经返回后,就不能再引用这些自动变量。在整个UNIX手册中,关于这一点有很多警告。

    程序7 - 7是一个名为open_data的函数,它打开了一个标准I / O流,然后为该流设置缓存。程序7-6 自动变量的不正确使用

    #include <stdio.h>



    #define DATAFILE "datafile"



    FILE *open_data(void)

    {

    FILE *fp;

    char databuf[BUFSIZ]; //setvbuf makes this the stdio buffer



    if ((fp = fopen(DATAFILE, "r")) == NULL)

    return NULL;

    if (setvbuf(fp, databuf, _IOLBF, BUFSIZ) != 0)

    return NULL;

    return fp;

    }



    int main(int argc, char *argv[])

    {

    FILE *fp;

    fp = open_data();

    return 0;

    }


    问题是:当open_data返回时,它在栈上所使用的空间将由下一个被调用函数的栈帧使用。但是,标准I / O库函数仍将使用原先为databuf在栈上分配的存储空间作为该流的缓存。这就产生了冲突和混乱。为了改正这一问题,应在全局存储空间静态地(staticextern ),或者动态地(使用一种a l l o c函数)为数组databuf分配空间。

  • 相关阅读:
    linux下设置SSH无密码登陆
    设置sudo权限
    集群重启后启动ambari-server访问Web页面无法启动集群解决
    使用Ambari部署hadoop集群
    centos7.6安装python3.7
    Locust
    测试框架(自然语言)
    Maven之(七)pom.xml配置文件详解
    git的使用
    elastic search(es)安装
  • 原文地址:https://www.cnblogs.com/shaoguangleo/p/2806010.html
Copyright © 2011-2022 走看看