例外は,現在実行しているスレッドで起こる特別な状態であり,その状態を認識して適切なアクションをとる実行コードが必要です。 このコードは,例外ハンドラと呼ばれます。
終了ハンドラは,制御フローが特定のコード本体を出るときに実行されるコードから構成されます。 終了ハンドラは,コード本体から出ることにより,メモリ・バッファの解放やロックのリリースなどのタスクを実行して,設定されたコンテキストをクリーンアップする場合に有効です。
この章では,次の項目について説明します。
Tru64 UNIX システムでは,『Alpha Architecture Reference Manual』で説明しているように,ハードウェアが例外をトラップして,それをオペレーティング・システムのカーネルに引き渡します。 カーネルは,不良メモリ・アクセスや算術トラップなどのハードウェア例外を,シグナルに変換します。 プロセスは,シグナルの引き渡しを可能にし,シグナル・ハンドラを設定して,プロセス内でシグナルを処理します。
『Calling Standard for Alpha Systems』では,Tru64 UNIX システムにおける例外的なイベントの処理を可能にする特別な構造とメカニズムについて,詳しく定義しています。 この規格に定義されている処理には,次のようなものがあります。
例外ハンドラが設定される方法
例外が引き起こされる方法
例外システムがハンドラを探索して呼び出す方法
ハンドラが例外システムに戻る方法
例外システムがスタックをトラバースして,プロシージャ・コンテキストを維持する方法
Tru64 UNIX の C コンパイラの構造化例外処理機能をサポートする実行時例外ディスパッチャは,規格に記述されているフレーム・ベースの例外ハンドラの 1 例です。 構造化例外処理の説明については,11.3 節を参照してください。
以降の各項で,『Calling Standard for Alpha Systems』に定義されている例外処理メカニズムをサポートする Tru64 UNIX の構成要素について簡単に説明します。
11.1.1 C コンパイラ構文
Tru64 UNIX の C コンパイラが提供する構文では,ユーザ定義およびシステム定義の例外条件に対して,コード領域を保護することができます。 このメカニズムは,構造化例外処理と呼ばれ,ユーザが例外ハンドラおよび終了ハンドラを定義するとともに,保護するコード領域を示すことができます。
c_excpt.h
ヘッダ・ファイルで定義しているシンボルと関数を使用すると,ユーザ例外処理コードは,現在の例外コードおよび例外について説明しているその他の情報を取得できます。
11.1.2 libexc ライブラリ・ルーチン
例外サポート・ライブラリ
/usr/ccs/lib/cmplrs/cc/libexc.a
では,次の機能を持つルーチンを提供しています。
ユーザ定義の例外を起こしたり,UNIX シグナルを例外に変換する機能
このようなルーチンには,次のものがあります。
exc_raise_status_exception
exc_raise_signal_exception
exc_raise_exception
exc_exception_dispatcher
exc_dispatch_exception
これらの例外管理ルーチンは,例外を適切なハンドラへディスパッチするメカニズムも提供しています。 11.3 節で説明する C 言語の構造化例外処理の場合,C 固有のハンドラは,ユーザ作成のコードを含むルーチンを呼び出して,実行するアクションを決定します。 ユーザ作成のコードは,例外を処理するか,または別のプロシージャを起動してその例外を処理します。
タスクからのプロシージャ起動レベルの仮想および実際の展開,およびハンドラまたはその他のユーザ・コードにおいて実行の継続を行う機能
このようなルーチンには,次のものがあります。
unwind
exc_virtual_unwind
RtlVirtualUnwind
exc_resume
exc_longjmp
exc_continue
exc_unwind
RtlUnwindRfp
展開ルーチンのいくつかは,展開時のハンドラ呼び出しもサポートして,特定のプロシージャ起動において,言語またはユーザがアイテムをクリーンアップできるようにします。
プロシージャ固有の情報にアクセスして,ルーチン内の任意のアドレスを対応するプロシージャ情報にマップする機能
この情報は展開を引き起こすのに十分なデータを含んでいるか,またはルーチンが例外を処理するかどうかを決定します。 このようなルーチンには,次のものがあります。
exc_add_pc_range_table
exc_remove_pc_range_table
exc_lookup_function_table_address
exc_lookup_function_entry
find_rpd
exc_add_gp_range
exc_remove_gp_range
exc_lookup_gp
C 言語の構造化例外ハンドラは,最後の 2 つのカテゴリのルーチンを呼び出して,ユーザ・コードが例外を処理して実行を再開し,ユーザ定義の例外ハンドラを探索してディスパッチできるようにします。
この処理については,11.3 節で説明しています。
/usr/ccs/lib/cmplrs/cc/libexc.a
で提供するルーチンについての詳細は,そのルーチンのリファレンス・ページを参照してください。
11.1.3 例外処理をサポートするヘッダ・ファイル
さまざまなヘッダ・ファイルで,例外処理システムおよびプロシージャ・コンテキストの操作をサポートする構造体を定義しています。
表 11-1
に,このようなヘッダ・ファイルの一覧を示します。
表 11-1: 例外処理をサポートするヘッダ・ファイル
ファイル | 説明 |
excpt.h |
例外コードの構造体および Tru64 UNIX の例外コードの数を定義する。
システム例外,コンテキスト・レコード,関連するフラグ,シンボリック定数,実行時プロシージャ・タイプ,および
libexc.a
で提供される関数用のプロトタイプも定義する。
詳細は
excpt (4) を参照。 |
c_excpt.h |
C 言語の構造化例外ハンドラおよび終了ハンドラが使用するシンボルを定義する。
また,例外コードを返す例外情報構造体と関数,その他の例外情報,および終了ハンドラが呼び出される状態に関する情報も定義する。
詳細は
c_excpt (4) |
machine/fpu.h |
IEEE 浮動小数点例外の引き渡しとその出現を記録する情報を探索するルーチン,ieee_set_fp_control
および
ieee_get_fp_control
用のプロトタイプを定義する。
また,これらのルーチンをサポートする構造体と定数も定義する。
詳細は
ieee (3) |
pdsc.h |
実行時プロシージャ記述子およびコード範囲記述子などの構造体を定義する。
これらは,『Calling Standard for Alpha Systems』に記述されているプロシージャ・タイプおよびフロー制御メカニズムに対して,実行時コンテキストを提供する。
詳細は
pdsc (4) |
ユーザ・プログラムは,通常,次の 2 つの方法で例外を起こします。
プログラムは,exc_raise_exception
または
exc_raise_status_exception
関数を呼び出すことによって,アプリケーション固有の例外を明示的に開始できる。
これらの関数を使用すると,呼び出し側のプロシージャは,例外について説明する情報を指定できます。
プログラムは,POSIX シグナルを例外に変換する特殊なシグナル・ハンドラ
exc_raise_signal_exception
をインストールできる。
exc_raise_signal_exception
関数は例外ディスパッチャを呼び出して,実行時スタックを探索し,現在または以前のスタック・フレームで設定された例外ハンドラを探します。
この場合,ハンドラに報告されるコードには,機能フィールドに EXC_SIGNAL,およびコード・フィールドにシグナル値が入ります。
コードのデータ構造体についての詳細は,
excpt
(4)excpt.h
ヘッダ・ファイルを参照してください。
注意
算術例外およびソフトウェア生成例外の正確な例外コードは,
signal.h
ヘッダ・ファイルに定義されており,code 引数でシグナル・ハンドラに渡されます。 特殊なシグナル・ハンドラexc_raise_signal_exception
は,例外ディスパッチャを呼び出す前に,このコードをExceptionRecord.ExceptionInfo[0]
に移動します。
11.3 節の例は,例外を明示的に起こして,シグナルを例外に変換する方法を示しています。
11.3 構造化例外ハンドラの作成
Tru64 UNIX の C コンパイラが提供する構造化例外処理機能を使用すると,特定のコード・シーケンスにおいて特定の例外条件が発生したときの処理を記述できます。
これらの機能は常に有効です (cc
コマンドの
-ms
オプションは不要です)。
構造化例外ハンドラを設定する構文は,次のとおりです。
try { try-body } except ( exception-filter) { exception-handler }
try-body は,例外ハンドラが保護する文または文のブロックです。 try 本体を実行中に例外が起きた場合,C 固有の実行時ハンドラは exception-filter を評価して,制御を関連する exception-handler に移すか,外部レベルの try 本体でハンドラの探索を継続するか,あるいは,例外が起きた場所から通常の実行を継続するかを決定します。
exception-filter は,try 本体を保護する例外ハンドラに関連する式です。 これは,単純式でも,式を評価する関数を呼び出しても構いません。 例外フィルタは,例外ディスパッチャが例外処理を終了するために,次のいずれかの整数値に評価されなければなりません。
< 0 (EXCEPTION_CONTINUE_EXECUTION)
例外ディスパッチャは例外を無視し,例外によって中断された実行スレッドを再開します。
継続不可能な例外の場合,ディスパッチャは
STATUS_NONCONTINUABLE_EXCEPTION
例外を起こします。
0 (EXCEPTION_CONTINUE_SEARCH)
例外ディスパッチャはハンドラの探索を継続します。
まず,現在のハンドラがネストされている可能性のある
try...except
ブロックを探索し,次に,実行時スタックおいて,現在のフレームの前のプロシージャ・フレームで定義されている
try...except
ブロックを探索します。
フィルタが例外を処理しないことを選択した場合,通常この値が返されます。
> 0 (EXCEPTION_EXECUTE_HANDLER)
例外ディスパッチャは制御を例外ハンドラに移し,ハンドラが見つかった実行時スタックのフレームで実行を継続します。 このプロセスは,例外処理と呼ばれ,現在のフレームの下のすべてのプロシージャ・フレームを展開して,それらのフレーム内で設定された終了ハンドラを実行します。
例外フィルタにおいて,次の 2 つの intrinsic 関数が,フィルタされている例外に関する情報にアクセスできます。
long exception_code (); Exception_info_ptr exception_info ();
exception_code
関数は,例外コードを返します。
exception_info
関数は,EXCEPTION_POINTERS
構造体へのポインタを返します。
このポインタを使用すると,例外が起きたときのマシン状態 (たとえば,システム例外やコンテキスト・レコード) にアクセスできます。
詳細は,
excpt
(4)c_excpt
(4)
exception_code
関数は,例外フィルタまたは例外ハンドラで使用できます。
しかし,exception_info
関数は,例外フィルタ内でのみ使用可能です。
例外ハンドラにおいて,exception_info
関数から返された情報を使用する必要がある場合は,その関数をフィルタ内で呼び出して,情報をローカルに格納しなければなりません。
フィルタ外で例外構造体を参照する必要がある場合は,同時にそれらをコピーしておかなければなりません。
これは,それらのストレージが,フィルタの実行中のみ有効なためです。
例外が起こると,例外ディスパッチャは,ハンドラが設定されているフレームに到達するまで,仮想的に実行時スタックを展開します。 ディスパッチャは最初に,例外が起きたときに現在のスタック・フレームであったスタック・フレームで例外ハンドラを探索します。
ハンドラがこのスタック・フレームにない場合には,ディスパッチャは,現在のスタック・フレームおよび介在するスタック・フレームをそのままにして,例外ハンドラを設定しているフレームに到達するまで,仮想的にスタックを (それ自身のコンテキストにおいて) 展開します。 その後,そのハンドラに関連する例外フィルタを実行します。
この例外ディスパッチのフェーズでは,ディスパッチャは実行時スタックを仮想的にのみ展開することに注意してください。
つまり,スタック上に存在している呼び出しフレームは依然その場所にあります。
例外ハンドラを見つけられない場合,またはすべてのハンドラで例外が再度起こる場合は,例外ディスパッチャはシステムのラスト・チャンス・ハンドラを呼び出します。
ラスト・チャンス・ハンドラの設定方法については,
exc_set_last_chance_handler
(3)
例外フィルタを Pascal スタイルのネストしたプロシージャのように処理することによって,例外処理コードはフィルタ式を
try...except
ブロックを含むプロシージャの有効範囲内で評価します。
これにより,そのフィルタを含むプロシージャのスタック・フレームまで,スタックが実際に展開されていなくても,フィルタ式は,そのフィルタを含むプロシージャのローカル変数にアクセスできます。
例外ハンドラを実行する前に (たとえば,例外フィルタが
EXCEPTION_EXECUTE_HANDLER
を返す場合),例外ディスパッチャは実行時スタックを実際に展開して,制御を例外ハンドラに移した結果として終了した
try...finally
ブロックに対して設定された終了ハンドラを実行します。
ディスパッチャが例外ハンドラを呼び出すのは,その後だけです。
exception-handler
は,例外条件を処理する複合文です。
これは,try...except
構文を含むプロシージャの有効範囲内で実行され,そのローカル変数にアクセスできます。
ハンドラは,例外の種類によって,さまざまな方法で例外に対応できます。
たとえば,エラーのログを取ったり,例外が生じる状況を修正することができます。
例外フィルタまたは例外ハンドラのどちらも,取得した例外情報を修正したり拡張し,C 言語の例外ディスパッチャに依頼して,外部の try 本体または以前の呼び出しフレームで設定された例外コードに新しい情報を引き渡すことができます。 この処理は,例外フィルタ内で行う方が簡単であり,最後に実行されているプロシージャのフレームで行なわれ,例外コンテキストは実行時スタックにそのまま残ります。 フィルタは,ディスパッチャに 0 を返すだけで処理を完了し,そのディスパッチャに次のハンドラの探索を継続することを要求します。
例外ハンドラが以前に設定されたハンドラを呼び出すためには,例外ハンドラは自分のコンテキストから,以前に設定されたハンドラで処理する別の例外を起こさなければなりません。
例 11-1
は,簡単な例外ハンドラを設定し,exc_raise_signal_exception
シグナル・ハンドラによって,例外に変換されたセグメンテーション違反シグナル (SIGSEGV) を処理する方法を示しています。
例 11-1: 構造化例外としての SIGSEGV シグナルの処理
#include <stdio.h> #include <stdlib.h> #include <signal.h> #include <excpt.h> void main(void) { struct sigaction act, oldact; char *x=0; /* * Set up things so that SIGSEGV signals are delivered. Set * exc_raise_signal_exception as the SIGSEGV signal handler * in sigaction. */ act.sa_handler = exc_raise_signal_exception; sigemptyset(&act.sa_mask); act.sa_flags = 0; if (sigaction(SIGSEGV, &act, &oldact) < 0) perror("sigaction:"); /* * If a segmentation violation occurs within the following try * block, the run-time exception dispatcher calls the exception * filter associated with the except statement to determine * whether to call the exception handler to handle the SIGSEGV * signal exception. */ try { *x=55; } /* * The exception filter tests the exception code against * SIGSEGV. If it tests true, the filter returns 1 to the * dispatcher, which then executes the handler; if it tests * false, the filter returns -1 to the dispatcher, which * continues its search for a handler in the previous run-time * stack frames. Eventually the last-chance handler executes. * Note: Normally the printf in the filter would be replaced * with a call to a routine that logged the unexpected signal. */ except(exception_code() == EXC_VALUE(EXC_SIGNAL,SIGSEGV) ? 1 : (printf("unexpected signal exception code 0x%lx\n", exception_code()), 0)) { printf("segmentation violation reported: handler\n"); exit(0); } printf("okay\n"); exit(1); }
次は,このプログラムの実行例です。
%
cc -std0 segfault_ex.c -lexc
%
a.out
segmentation violation reported in handler
例 11-2
は
例 11-1
と同様に,シグナル例外の処理方法を示していますが,この場合は SIGFPE を処理します。
この例では,さらに,IEEE 浮動小数点例外であるゼロによる浮動除算を,ieee_set_fp_control()
への呼び出しにより使用可能にする方法,およびハンドラがシステム例外レコードを読み取ることにより詳細な情報を取得する方法を示します。
例 11-2: 構造化例外としての IEEE 浮動小数点 SIGFPE の処理
#include <stdio.h> #include <stdlib.h> #include <signal.h> #include <excpt.h> #include <machine/fpu.h> #include <errno.h> int main(void) { Exception_info_ptr except_info; system_exrec_type exception_record; long code; struct sigaction act, oldact; unsigned long float_traps=IEEE_TRAP_ENABLE_DZE; double temperature=75.2, divisor=0.0, quot, return_val; /* * Set up things so that IEEE DZO traps are reported and that * SIGFPE signals are delivered. Set exc_raise_signal_exception * as the SIGFPE signal handler. */ act.sa_handler = exc_raise_signal_exception; sigemptyset(&act.sa_mask); act.sa_flags = 0; if (sigaction(SIGFPE, &act, &oldact) < 0) perror("sigaction:"); ieee_set_fp_control(float_traps); /* * If a floating divide-by-zero FPE occurs within the following * try block, the run-time exception dispatcher calls the * exception filter associated with the except statement to * determine whether the SIGFPE signal exception is to be * handled by the exception handler. */ try { printf("quot = IEEE %.2f / %.2f\n",temperature,divisor); quot = temperature / divisor; } /* * The exception filter saves the exception code and tests it * against SIGFPE. If it tests true, the filter obtains the * exception information, copies the exception record structure, * and returns 1 to the dispatcher which then executes the handler. * If the filter's test of the code is false, the filter * returns 0 to the handler, which continues its search for a * handler in previous run-time frames. Eventually the last-chance * handler executes. Note: Normally the filter printf is replaced * with a call to a routine that logged the unexpected signal. */ except((code=exception_code()) == EXC_VALUE(EXC_SIGNAL,SIGFPE) ? (except_info = exception_info(), exception_record = *(except_info->ExceptionRecord), 1) : (printf("unexpected signal exception code 0x%lx\n", exception_code()), 0)) /* * The exception handler follows and prints out the signal code, * which has the following format: * * 0x 8 0ffe 0003 * | | | | * hex SIGFPE EXC_OSF facility EXC_SIGNAL */ { printf("Arithmetic error\n"); printf("exception_code() returns 0x%lx\n", code); printf("EXC_VALUE macro in excpt.h generates 0x%lx\n", EXC_VALUE(EXC_SIGNAL, SIGFPE)); printf("Signal code in the exception record is 0x%lx\n", exception_record.ExceptionCode); /* * To find out what type of SIGFPE this is, look at the first * optional parameter in the exception record. Verify that it is * FPE_FLTDIV_TRAP). */ printf("No. of parameters is %lu\n", exception_record.NumberParameters); printf("SIGFPE type is 0x%lx\n", exception_record.ExceptionInformation[0]); /* * Set return value to IEEE_PLUS_INFINITY and return. */ if (exception_record.ExceptionInformation[0] == FPE_FLTDIV_TRAP) { *((long*)&return_val) = IEEE_PLUS_INFINITY; printf("Returning 0x%f to caller\n", return_val); return 0; } /* * If this is a different kind of SIGFPE, return gracelessly. */ else return -1; } /* * We get here only if no exception occurred in the try block. */ printf("okay: %f\n", quot); exit(1); }
次は,このプログラムの実行例です。
%
cc -std0 sigfpe_ex.c -lexc
%
a.out
quot = IEEE 75.20 / 0.00 Arithmetic error exception_code() returns 0x80ffe0003 The EXC_VALUE macro in excpt.h generates 0x80ffe0003 The signal code in the exception record is 0x80ffe0003 No. of parameters is 1 SIGFPE type is 0x4 Returning 0xINF to caller
プロシージャ (または相互に関係のあるプロシージャのグループ) は,try...except
構造をいくつでも含むことができ,また,これらの構造はネストしても構いません。
try...except
ブロック内で例外が起こると,システムはそのブロックに関連する例外ハンドラを呼び出します。
例 11-3
は,2 つのプライベートな例外コードによって定義され,最も内側の try ブロック内でこれら 2 つの例外のどちらかを起こす,複数の
try...except
ブロックの動作を示しています。
例 11-3: 複数の構造化例外ハンドラ
#include <excpt.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #define EXC_NOTWIDGET EXC_VALUE(EXC_C_USER, 1) #define EXC_NOTDECWIDGET EXC_VALUE(EXC_C_USER, 2) void getwidgetbyname(char *); /* * main() sets up an exception handler to field the EXC_NOTWIDGET * exception and then calls getwidgetbyname(). */ void main(int argc, char *argv[]) { char widget[20]; long code; try { if (argc > 1) strcpy(widget, argv[1]); else { printf("Enter widget name: "); gets(widget); } getwidgetbyname(widget); } except((code=exception_code()) == EXC_NOTWIDGET) { printf("Exception 0x%lx: %s is not a widget\n", code, widget); exit(0); } } /* * getwidgetbyname() sets up an exception handler to field the * EXC_NOTDECWIDGET exception. Depending upon the data it is * passed, its try body calls exc_raise_status_exception() to * generate either of the user-defined exceptions. */ void getwidgetbyname(char* widgetname) { long code; try { if (strcmp(widgetname, "foo") == 0) exc_raise_status_exception(EXC_NOTDECWIDGET); if (strcmp(widgetname, "bar") == 0) exc_raise_status_exception(EXC_NOTWIDGET); } /* * The exception filter tests the exception code against * EXC_NOTDECWIDGET. If it tests true, the filter returns 1 * to the dispatcher; if it tests false, the filter returns -1 * to the dispatcher, which continues its search for a handler * in the previous run-time stack frames. When the generated * exception is EXC_NOTWIDGET, the dispatcher finds its handler * in main()'s frame. */ except((code=exception_code()) == EXC_NOTDECWIDGET) { printf("Exception 0x%lx: %s is not " "a Hewlett-Packard-supplied widget\n", code, widgetname); exit(0); } printf("widget name okay\n"); }
次は,このプログラムの実行例です。
%
cc raise_ex.c -lexc
%
a.out
Enter widget name:
foo
Exception 0x20ffe009: foo is not a Hewlett-Packard-supplied widget
%
a.out
Enter widget name:
bar
Exception 0x10ffe009: bar is not a widget
cc
コンパイラは,保護されたコード本体から制御が渡されると,指定された終了コードのブロックが必ず実行されるようにします。
終了コードは,制御フローが保護されたコードを出る方法にかかわらず実行されます。
たとえば,保護されたコード本体の実行中に,例外またはその他のエラーが生じても,終了ハンドラは,クリーンアップ・タスクが確実に実行されるようにします。
終了ハンドラの構文は,次のとおりです。
try { try-body } finally { termination-handler }
try-body
は,複合文として表現されたコードであり,終了ハンドラが保護します。
try 本体は,文のブロックまたはネストしたブロックの集まりでも構いません。
これには,次の文を含むことができます。
この文は,ブロックから直ちに出て,終了ハンドラを実行します。
leave;
注意
Tru64 UNIX の
longjmp()
ルーチンは,展開操作を使用しません。 したがって,フレーム・ベースの例外処理がある場合には,try-body または termination-handler からlongjmp()
を使用しないでください。 代わりに,展開操作を介してインプリメントされるexc_longjmp()
を使用してください。
termination-handler
は,try 本体が正常終了したか異常終了したかにかかわらず,制御フローが保護された try 本体を出ると実行される複合文です。
ブロック内の最後の文が実行された (つまり,本体の "}" に到達した) とき,保護された本体は正常終了したとみなされます。
leave
文を使用しても,正常終了します。
制御フローがその他の方法で保護された本体を出ると,その本体は異常終了します。
たとえば,例外や,return
,goto
,break
,continue
などの制御文で保護された本体を出た場合です。
終了ハンドラは次の intrinsic 関数を呼び出して,保護された本体が正常終了したか異常終了したかを判断できます。
int abnormal_termination ();
try 本体がシーケンシャルに ("}" に到達することによって) 完了した場合,abnormal_termination
関数は 0 を返し,そうでない場合は 1 を返します。
終了ハンドラ自体は,シーケンシャルに終了することも,ハンドラの外に制御を渡して終了することもできます。 シーケンシャルに ("}" に到達することによって) 終了する場合,その後の制御フローは,次のように try 本体の終了方法に依存します。
try 本体が正常に終了した場合,完全な
try...finally
ブロックの次の文から実行が継続される。
本体の外への明示的な飛び越しによって try 本体が異常終了した場合,その飛び越しが完了する。
ただし,1 つ以上の
try...finally
文を含む本体にその飛び越しがある場合は,制御が最後に飛び越しのターゲットに渡される前に,終了ハンドラが呼び出されます。
try 本体が展開により異常終了した場合,例外ハンドラへの飛び越し,または
exc_longjmp
呼び出しにより,制御は C 実行時例外ハンドラへ戻る。
この例外ハンドラは,展開のターゲットへ飛び越す前に,要求によって終了ハンドラの呼び出しを継続します。
例外フィルタと同様に,終了ハンドラは Pascal スタイルのネストしたプロシージャとして処理され,実行時スタックからフレームを削除せずに実行されます。 終了ハンドラは,プロシージャで宣言されているローカル変数に,このようにしてアクセスできます。
異常終了 (および例外) は,ほとんどのプログラムにとって,通常の制御フロー外と考えられるため,異常終了の処理には実行コストがかかります。 try 本体外への明示的な飛び越しは,異常終了とみなされることを覚えておいてください。 正常終了は単純な場合であり,実行時にかかるコストが少なくてすみます。
場合によっては,try 本体外への飛び越しを
leave
文 (制御を最も内側の try 本体の終わりに移す) に置き換えて,try...finally
ブロックを完了した後に状態変数を検査すると,このコストを回避できます。
終了ハンドラ自体は,制御の引き渡し (たとえば,goto
,break
,continue
,return
,exc_longjmp
,または例外の発生) によって,シーケンシャルでない方法で終了する (たとえば,展開の異常終了) ことがあります。
この制御の引き渡しが別の
try...finally
ブロックに存在する場合,終了ハンドラが実行されます。
例 11-4
は,例外によって最も内側の try 本体が終了するとき,終了ハンドラおよび例外ハンドラが実行される順序を示しています。
例 11-4: 例外による try 本体の異常終了
#include <stdio.h> #include <signal.h> #include <excpt.h> #include <errno.h> #define EXC_FOO EXC_VALUE(EXC_C_USER, 1) signed int foo_except_filter(void) { printf("2. The exception causes the exception filter " "to be evaluated.\n"); return 1; } void main (void) { try { try { printf("1. The main body executes.\n"); exc_raise_status_exception(EXC_FOO); } finally { printf("3. The termination handler executes " "because control will leave the " "try...finally block to \n"); } } except(foo_except_filter()) { printf("4. execute the exception handler.\n"); } }
次に示すのは,このプログラムの実行例です。
% cc segfault_ex2.c -lexc % a.out 1. The main body executes. 2. The exception causes the exception filter to be evaluated. 3. The termination handler executes because control will leave the try...finally block to 4. execute the exception handler.