【光之魔法—程序的时光倒流】
简述
非局部跳转,一个在C中极具争议的机制;
使用非局部跳转可以实现从一个函数体内跳转到另一个(事先登记过的)函数体内,而不会导致堆栈混乱;
每个函数的调用上下文可都是按照严格的顺序保存在函数堆栈中;
示例代码
C
写一个demo,思考一下下面的程序输出什么;
#include <setjmp.h>
#include <stdio.h>
jmp_buf jmp;
int startjmp() {
longjmp(jmp,1);
return 1;
}
int main(){
if (setjmp(b))
printf("go main...");
else {
printf("start jump...");
startjmp();
}
return 0;
}
编译执行下:
gcc jump.c -o jump
./jump
// output
start jump...go main...
Golang
golang利用goto实现【jmp.go】:
package main
func main(){
var j int= 0
label :for {
j += 1
println("jump:[",j,"]")
goto label
}
println(" main run end ...")
}
~
run and output:
go version
go version go1.14.12 linux/amd64
go run jmp.go
...
jump:[ 598329 ]
jump:[ 598330 ]
jump:[ 598331 ]
jump:[ 598332 ]
jump:[ 598333 ]
jump:[ 598334 ]
jump:[ 598335 ]
jump:[ 598336 ]
...
// 持续输出jump
这是为啥?
总结
这是因为longjmp让程序跳转到setjmp的位置,并且将setjmp的返回设置为longjmp的第二参数值(例子中为1);所以输出start jump后输出了go main;实现了类似“时光倒流”的效果;
打个不太恰当的比喻,setjmp就是给跳转标记了位置,而longjmp就是起跳。
那么问题来了,下面这段程序输出是?
#include <setjmp.h>
#include <stdio.h>
jmp_buf jmp;
int startjmp() {
longjmp(jmp,1);
return 1;
}
int main(){
int j = 1;
if (setjmp(jmp))
printf("go main...");
else if (setjmp(jmp)==1){
j += 1;
printf(" jump:[%d] ",j);
startjmp();
}
else {
printf("start jump...");
printf("jump return: %d",startjmp());
}
return 0;
}
提示就这么多了【不要看错了重点(doge)】。