局部跳转和非局部跳转

  1. 局部跳转goto
    虽然很多书籍对goto建议 “谨慎使用, 或者根本不用”, 然而当一个函数的多个分支需要一个统一出口时, goto语句就非常必要了. Linux之父Linus在Linux中大量使用goto, 也是在启示着我们可以合理使用goto语句.
    在高通的qsdk中的time_genoff_qmi.c就有这么一段code.
int time_genoff_operation(time_genoff_info_type *pargs)
{
        if (connect(sock_id, (struct sockaddr *)&time_socket, length) == -1) {
                TIME_LOGE("Lib:%s: Connection failed !!\n", __func__);
                goto error_close_socket;
        }

        if (pargs->operation == T_GET) {

                if (send(sock_id, &to_send, sizeof(to_send), 0) < 0) {
                        goto error_close_socket;
                } else {
                        if (recv(sock_id, (void *)&to_send, sizeof(to_send), 0) < 0) {
                                goto error_close_socket;
                        }
                        if (to_send.result != 0) {
                                goto error_close_socket;
                        }
                }

        }
        return 0;
error_close_socket:
        close(sock_id);
        return -EINVAL;
}
  • goto的基本语法
    goto label;
  • goto中获取变标签的地址用&&
#include <stdio.h>
#include <stdint.h>

int main(int argc, char **argv)
{
        void *ptr = &&lble;
        goto *ptr;
        printf("here we are\n");
lble:
        printf("reach goto\n");
        return 0;
}

输出:

pam:~/test/jmp$ ./goto
reach goto
  1. 非局部跳转
  • setjmp 和 longjmp
    在C中, goto语句是不能跨越函数的, 而执行这种类型跳转功能的是函数setjmp 和 longjmp, 这两个函数对处理发生在很深层次中的出错情况是很有用的.
#include        <errno.h>
#include        <stdarg.h>
#include        <setjmp.h>
#include        <signal.h>
#include        <unistd.h>
#include        <stdio.h>
#include        <stdlib.h>


#define MAXLINE 4096            /* max line length */

static jmp_buf  env_alrm;

unsigned int    sleep2(unsigned int);
static void             sig_int(int);
static void     err_sys(const char *fmt, ...);
static void     err_doit(int errnoflag, int error, const char *fmt, va_list ap);

int main(void)
{
        unsigned int    unslept;

        if (signal(SIGINT, sig_int) == SIG_ERR)
                err_sys("signal(SIGINT) error");
        unslept = sleep2(5);
        printf("sleep2 returned: %u\n", unslept);
        exit(0);
}

static void sig_int(int signo)
{
        int             i, j;
        volatile int    k;

        /*
         * Tune these loops to run for more than 5 seconds
         * on whatever system this test program is run.
         */
        printf("\nsig_int starting\n");
        for (i = 0; i < 300000; i++)
                for (j = 0; j < 4000; j++)
                        k += i * j;
        printf("sig_int finished\n");
}

/*
 * Fatal error related to a system call.
 * Print a message and terminate.
 */
static void err_sys(const char *fmt, ...)
{
        va_list         ap;

        va_start(ap, fmt);
        err_doit(1, errno, fmt, ap);
        va_end(ap);
        exit(1);
}

/*
 * Print a message and return to caller.
 * Caller specifies "errnoflag".
 */
static void err_doit(int errnoflag, int error, const char *fmt, va_list ap)
{
        char    buf[MAXLINE];

        vsnprintf(buf, MAXLINE-1, fmt, ap);
        if (errnoflag)
                snprintf(buf+strlen(buf), MAXLINE-strlen(buf)-1, ": %s", strerror(errno));
        strcat(buf, "\n");
        fflush(stdout);         /* in case stdout and stderr are the same */
        fputs(buf, stderr);
        fflush(NULL);           /* flushes all stdio output streams */
}

static void sig_alrm(int signo)
{
        longjmp(env_alrm, 1);
        printf("%s(%d)\n", __FILE__, __LINE__);
}

static void print(void)
{
        longjmp(env_alrm, 3);
        printf("%s(%d)\n", __FILE__, __LINE__);
}

unsigned int sleep2(unsigned int seconds)
{
        int ret;
        printf("%s(%d)\n", __FILE__, __LINE__);
        if (signal(SIGALRM, sig_alrm) == SIG_ERR)
                return(seconds);
        ret = setjmp(env_alrm);
        if ( ret == 0) {
                alarm(seconds);         /* start the timer */
                printf("%s(%d): ret = %d\n", __FILE__, __LINE__, ret);
                pause();                        /* next caught signal wakes us up */
        }
        else if (ret == 1)
        {
                printf("%s(%d): ret = %d\n", __FILE__, __LINE__, ret);
                print();
        }
        else
        {
                printf("%s(%d): ret = %d\n", __FILE__, __LINE__, ret);
        }

        printf("%s(%d)\n", __FILE__, __LINE__);

        return(alarm(0));               /* turn off timer, return unslept time */
}

正常情况下输出:

pam:~/test/jmp$ ./setjmp
setjmp.c(92)
setjmp.c(98): ret = 0
setjmp.c(103): ret = 1
setjmp.c(108): ret = 3
setjmp.c(111)
sleep2 returned: 0

解读:
void longjmp(jmp_buf env, int val) 中的第二参数回作为 int setjmp(jmp_buf env) 的返回值, 主程序注册SIGINT信号及其处理函数后调用sleep2(5), sleep2 中注册SIGALRM及其信号处理函数, setjmp之前没有longjmp, 所以返回值为0, 然后alarm(5)并等待pause等待信号才继续往下走, 5秒后发出SIGALRM, 调用信号handler: sig_alrm, 运行 longjmp(env_alrm, 1)再次从ret = setjmp(env_alrm);处开是执行, 此时ret=1, 计入ret==1分支, 在调用print(); 再次跳到ret = setjmp(env_alrm);开始往下执行, 此时进入else分支.

5秒内按ctrl-c输出可能是:

pam:~/test/jmp$ ./setjmp
setjmp.c(92)
setjmp.c(98): ret = 0
^C
sig_int starting
sig_int finished
setjmp.c(111)
sleep2 returned: 1

或者

pam:~/test/jmp$ ./setjmp
setjmp.c(92)
setjmp.c(98): ret = 0
^C
sig_int starting
setjmp.c(103): ret = 1
setjmp.c(108): ret = 3
setjmp.c(111)
sleep2 returned: 0

造成上述原因是,

 for (i = 0; i < 300000; i++)
          for (j = 0; j < 4000; j++)
                  k += i * j;

的延时时间比5秒长不到一点点, 如果快速按下的ctrl-c的话就就没有ret=1和ret=3的情况, 如果把300000改成3000000, 4000改成40000的话(远远大于5s), 就总是能输出ret=1和ret=3.

  • sigsetjmp 和 siglongjmp
    https://blog.csdn.net/wllinux12138/article/details/82284343
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值