最近在面試時被考官問了一題,如何用C實作出意外處理的機制?
在其他語言,exception都有其實作的辦法,例如C++中的try-catch語句,但是在C中並沒有實作這樣功能,因此必須使用一些小手段來達成這個目的,我們寫的函式庫交於他人使用時,不至於一點錯誤就當掉。
方法一: 傳入error變數記錄錯誤狀況
我們要在傳入function的變數中,多傳遞一個error變數,儲存錯誤代碼,當function執行結束後,我們檢查這個變數,再對應相對的處理方案。
一下我們用除法作為一個示範,當除數為0時我們要拋出意外,而其他時候則正常執行。
實作function
int div(int dividend, int divisor, int *err) {
if(divisor==0) { // 發生錯誤
*err = 1; // 錯誤代碼
return;
}
else { // 正常執行
*err = 0;
return dividend/divisor;
}
}
使用此function
int err = 0;
int dividend = 20;
int divisor = 0;
int result = div(dividend, divisor, &err); // 執行這個function
switch(err) {
case 0:
// 正常執行時的情況
break;
case 1:
// 除數為0的錯誤處理
break;
}
方法二: 使用setjmp與longjmp方法
在C中的goto僅能在函式內部進行跳轉,如果想要跳轉到函式外部則必須使用setjmp與longjmp的組合,使用setjmp設定錨點,再利用longjmp進行跳轉的動作,我們可以使用這個強制跳轉的特性來做到意外處理的機制。
首先要知道什麼是一個callstack
void layer1(int i) {
return layer2(i)*2;
}
void layer2(int i) {
return 100/i;
}
int main() {
layer1(x);
}
如上面的程式碼,我們依序的執行main->layer1->layer2,因此call stack長這樣 注意的是當程式碼跳轉的時候記憶體的洩漏問題,例如我們在layer2突然跳回main,則layer1的記憶體就洩露了,因此我們可以依賴jmp_buf變數來幫我們自動處理這個問題。
因此一個含有意外處理的程式應該會長這樣
#include <stdio.h>
#include <setjmp.h>
jmp_buf jmpbuffer; // 用來儲存程式跳轉期間的資訊
void layer1(int i) {
return layer2(i)*2;
}
void layer2(int i) {
if(i==0) longjmp(jmpbuffer, 1); // 發生錯誤時,跳轉回main
return 100/i;
}
int main() {
int jmpVal = setjmp(jmpbuffer);
if(jmpVal==0) {
// 第一次執行時
layer1(0);
}
else (jmpVal==1) {
// 發生意外狀況時的處理
fprintf(stderr, "divisor can't equal to 0\n");
}
// 繼續執行下面的程式
}
References:
1. How to throw an exception in C?