goto 基本用法
#include <stdio.h>
int main(int argc,char* argv[])
{
int *p = (int *)malloc(sizeof(int));
if (!P) {
goto error;
}
return 0;
error:
return -1;
}
用法:
- 给定一个标签名
- 直接goto标签名
标签名地址获取
#include <stdio.h>
int main(int argc,char* argv[])
{
void *p = NULL;
void *perr = &&err;
lable:
p = &&lable;
printf("%p\n",p);
err:
return 0;
}
获取地址直接使用 ’ && '即可。有两个特殊点
- 在定义标记的后面,不能再定义局部变量。gcc报错为 “a label can only be part of a statement and a declaration is not a statement”
- 获取标记地址没有要在标记后面的说法,如上例的 ‘perr’ 变量可以获取在它之后的标记地址
标记地址的作用
转成函数地址
标记地址是代码段的地址,可以试着当函数使用
#include <stdio.h>
int main(int argc,char* argv[])
{
void (*p_fun)() = (void (*)())&&end;
p_fun();
printf("aaaaaaa\n");
end:
printf("bbbbbbb\n");
printf("aaaaaaa\n");
printf("aaaaaaa\n");
return 0;
}
输出:
bbbbbbb
aaaaaaa
aaaaaaa
这段代码堆栈没有平衡。只是测试标记地址
goto标记地址
#include <stdio.h>
#include <stdlib.h>
int main(int argc,char *argv[])
{
void *addr = &&end;
goto *addr;
printf("aaaaaaa\n");
end:
printf("bbbbbbb\n");
printf("aaaaaaa\n");
printf("aaaaaaa\n");
return 0;
}
要goto地址时,在保存地址变量加 ‘ * ’即可
动态goto
对比之前使用的goto,goto的使用是对应固定的标签。但我们可以把标签的地址保存在变量中,然后去goto这个变量,为动态goto提供了必要的条件。
动态goto的例子:
#include <stdio.h>
#include <string.h>
enum {
ADD,
SUB,
MUL,
DIV,
END
};
int main(int argc,char *argv[])
{
static const void *const disptab[] = {
&&L_ADD,
&&L_SUB,
&&L_MUL,
&&L_DIV,
&&L_END
};
int code[] = {ADD,ADD,MUL,MUL,END};
int i = 0;
int sum = 0;
goto *disptab[code[i++]];
L_ADD:
sum += 2;
goto *disptab[code[i++]];
L_SUB:
sum -= 2;
goto *disptab[code[i++]];
L_MUL:
sum *= 2;
goto *disptab[code[i++]];
L_DIV:
sum /= 2;
goto *disptab[code[i++]];
L_END:
printf("sum = %d\n",sum);
return 0;
}
这段代码等价与
for()
{
switch()
{
case ADD:
sum += 2;
break;
case SUB:
sum -= 2;
break;
case MUL:
sum *= 2;
break;
case DIV:
sum /= 2;
break;
case END:
}
}
这个两段代码是仿照lua虚拟机的代码。在lua5.1采用的是for-switch,在最新的lua5.4采用的是动态goto。
参考:
lua5.4 goto代码
lua5.1 for-switch代码(GitHub上已经无5.1的代码可以在官网上下载整个源码)