【C言語】setjmp.hを使ってエラーハンドリングや特殊な制御フローを実装【標準ライブラリ】
setjmp.hとは
C言語の setjmp.h
は、プログラムのある時点での状態(レジスタやスタック情報)を保存し、
その状態に戻ることを可能にするヘッダファイルです。
setjmp
を使って現在の状態を記録し、longjmp
でその状態に復帰できます。
これは、エラーハンドリングや一部の特殊な制御フローで活用されます。
setjmpとlongjmpの基本
setjmp.h
には主に以下の2つの関数が含まれています。
setjmp関数
#include <setjmp.h>
jmp_buf buf;
int main() {
if (setjmp(buf) == 0) {
printf("最初の実行\n");
} else {
printf("longjmpによって戻ってきた\n");
}
return 0;
}
setjmp
は、現在のスタック状態を jmp_buf
型の変数に保存します。
その後 longjmp
を呼び出すと、保存した位置にジャンプできます。
longjmp関数
#include <setjmp.h>
#include <stdio.h>
jmp_buf buf;
void func() {
printf("func内でlongjmpを呼ぶ\n");
longjmp(buf, 1); // setjmp(buf) が1を返す
}
int main() {
if (setjmp(buf) == 0) {
printf("最初の実行\n");
func();
} else {
printf("longjmpによって戻ってきた\n");
}
return 0;
}
setjmp.hの典型的な使い方
setjmp
と longjmp
は、次のような場面で役立ちます。
1. 深い関数呼び出しからの脱出
#include <setjmp.h>
#include <stdio.h>
jmp_buf buf;
void deep_function(int level) {
printf("Level %d\n", level);
if (level == 5) {
longjmp(buf, 1);
} else {
deep_function(level + 1);
}
}
int main() {
if (setjmp(buf) == 0) {
deep_function(1);
} else {
printf("深い関数呼び出しから復帰\n");
}
return 0;
}
2. 状態管理の簡略化
#include <setjmp.h>
#include <stdio.h>
jmp_buf buf;
void process_stage(int stage) {
printf("処理中: ステージ %d\n", stage);
if (stage == 3) {
longjmp(buf, 1);
}
}
int main() {
int stage = 1;
if (setjmp(buf) == 0) {
for (; stage <= 5; stage++) {
process_stage(stage);
}
} else {
printf("ステージ %d でエラーが発生し復帰\n", stage);
}
return 0;
}
エラーハンドリングへの応用
例えば、例外処理のようにエラーハンドリングに利用できます。
#include <setjmp.h>
#include <stdio.h>
jmp_buf error_buf;
void error_function() {
printf("エラー発生!\n");
longjmp(error_buf, 1);
}
int main() {
if (setjmp(error_buf) == 0) {
printf("通常処理開始\n");
error_function();
} else {
printf("エラーハンドリング完了\n");
}
return 0;
}
簡単なコルーチンの実装
setjmp
を使うことで、簡単なコルーチンを実装できます。
#include <setjmp.h>
#include <stdio.h>
jmp_buf co1, co2;
void coroutine1() {
printf("Coroutine 1: Start\n");
longjmp(co2, 1);
printf("Coroutine 1: End\n");
}
void coroutine2() {
if (setjmp(co2) == 0) {
return;
}
printf("Coroutine 2: Running\n");
longjmp(co1, 1);
}
int main() {
if (setjmp(co1) == 0) {
coroutine2();
coroutine1();
} else {
printf("Coroutine switching complete\n");
}
return 0;
}
使用上の注意点
setjmp
はローカル変数の値を保証しない。longjmp
の戻り値は0にならないようにする(0を渡すと自動的に1になる)。- メモリ確保やファイルハンドルの解放などのリソース管理に注意。
setjmp
と longjmp
は強力な機能ですが、適切に使わないとコードの可読性が低下します。
エラーハンドリングのような特定の用途でのみ使用するのが望ましいです。