0%

Introduction setjmp / longjmp

這一組API雖然在Coding上不會直接用上,不過要實做Coroutine,模擬Exception的時候,總部會少了他。紀錄一下>
setjmp/longjmp很像Goto,不過比Goto更強,可以跳躍至任何地方,Goto不能跳躍至函數外層。直接看例子吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>  
#include <setjmp.h>
static jmp_buf buf;
void second(void) {
puts("second");
longjmp(buf, 1);
}
void first(void) {
second();
puts("first");
}
int main() {
if (!setjmp(buf)) {
first();
}
else {
puts("main");
}

return 0;
}

叢書出的結果我們可以猜到程式怎麼運作

1
2
second
main

首先,在setjmp那邊,紀錄目前Frame Context的內容,第一次執行的時候,回傳值為0,因此會呼叫first。
呼叫到second()時,遇到了longjmp,因此恢復原先的Frame Context,將回傳值設成1,回到原先main branc中而印出main。
很明顯的,first在這邊由於控制權轉移的發生,永遠不會發生。

Exception Handling

雖然C語言沒有直接支援Exception Handling,可以用這種方式模擬

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <stdio.h>  
#include <setjmp.h>

static jmp_buf buf;

int func(int a, int b) {
if (b == 0)
// can't divide by 0
longjmp(buf, -3);
return a / b;
}

int divide(int a, int b)
{
return func(a, b);
}
int main()
{
int a, b;
while (scanf("%d %d", &a, &b) == 2)
{
if (setjmp(buf))
puts("Cannot / 0");
else
printf("%d\n", divide(a, b));
}
}

GCC中的例外處理方式中的SJLJ,就是基於者兩個函數下發展的,可以參考Stackoverflow

至於Coroutine的作法可以獨立寫一篇,之後再補上吧。