- 局部跳转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
- 非局部跳转
- 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