コンピューティングおよびコンピュータプログラミングにおいて、例外処理とは、プログラムの実行中に発生する例外(特別な処理を必要とする異常または例外的な状況)に対応するプロセスです。一般的に、例外が発生すると通常の実行フローが中断され、事前に登録された例外ハンドラが実行されます。その具体的な方法は、例外がハードウェア例外かソフトウェア例外か、またソフトウェア例外の実装方法によって異なります。
例外はコンピュータシステムのさまざまなレイヤーで定義され、代表的なレイヤーとしては、CPU定義の割り込み、オペレーティングシステム(OS)定義のシグナル、プログラミング言語定義の例外などがあります。各レイヤーは例外処理の方法が異なりますが、例外処理は相互に関連している場合もあります。例えば、CPU割り込みをOSシグナルに変換することができます。例外の中には、特にハードウェア関連の例外など、処理が非常にスムーズに行われるものもあり、中断された箇所から実行を再開できる場合もあります。
例外の定義は、各プロシージャには前提条件、つまり「正常に」終了する一連の状況があるという観察に基づいています。 [ 1 ]例外処理メカニズムは、この前提条件が破られた場合([ 1 ]、例えばプロシージャが異常な引数セットで呼び出された場合など)に、プロシージャが例外を発生させることを可能にします。 [ 2]そして、例外処理メカニズムがその例外を処理します。 [ 3 ]
前提条件と例外の定義は主観的なものです。「通常」の状況のセットはプログラマーによって完全に定義されます。たとえば、プログラマーはゼロ除算を未定義とみなして例外としたり、ゼロを返す、または特別な「ゼロ除算」値を返すなどの動作を考案したりします(例外の必要性を回避します)。[ 4 ]一般的な例外には、無効な引数(たとえば、値が関数のドメイン外である)、[ 5 ]利用できないリソース(ファイルが見つからない、[ 6 ]ネットワークドライブのエラー、[ 7 ]またはメモリ不足エラー[ 8 ]など)、またはルーチンが特別な処理を必要とする通常の状態を検出した場合(たとえば、注意、ファイルの終わり)などがあります。[ 9 ]社会的圧力は、例外の範囲と例外処理メカニズムの使用に大きな影響を与えます。つまり、「コアライブラリでよく見られる使用例、技術書、雑誌記事、オンラインディスカッションフォーラムのコード例、組織のコード標準」などです。[ 10 ]
例外処理は、正常な戻り値と誤った戻り値を区別するメカニズムによって、半述語問題を解決します。C のように例外処理が組み込まれていない言語では、ルーチンは一般的な戻りコードやerrnoパターンなど、別の方法でエラーを通知する必要があります。[ 11 ]広い視点で見ると、エラーは例外の適切な部分集合とみなすことができ、[ 12 ] errno などの明示的なエラーメカニズムは、例外処理の(冗長な)形式とみなすことができます。[ 11 ]「例外」という用語は、「エラー」よりも好ましいです。なぜなら、何かが間違っていることを意味しないからです。ある手順やプログラマーがエラーとみなす状態が、別の手順やプログラマーによってそうみなされない場合もあります。[ 13 ]
「例外」という用語は、「異常」というニュアンスから、例外が発生することが異常または普通ではないことを示唆するため、誤解を招く可能性があります。 [ 14 ]実際には、例外が発生することはプログラム内では正常で通常の状況である可能性があります。[ 13 ]例えば、連想配列のルックアップ関数が、キーに関連付けられた値がない場合は例外をスローするとします。コンテキストによっては、この「キーが存在しない」例外は、ルックアップが成功するよりもはるかに頻繁に発生する可能性があります。[ 15 ]
最初のハードウェア例外処理は、 1951年のUNIVAC Iで発見されました。算術オーバーフローは、アドレス0で2つの命令を実行し、制御を転送したり、結果を修正したりすることができました。[ 16 ] ソフトウェア例外処理は1960年代と1970年代に開発されました。例外処理はその後、1980年代以降、多くのプログラミング言語で広く採用されました。
ハードウェアに関して、例外の正確な意味については明確な合意はありません。[ 17 ]実装の観点からは、割り込みとまったく同じように処理されます。プロセッサは現在のプログラムの実行を停止し、その例外または割り込み条件の割り込みベクタテーブルで割り込みハンドラを検索し、状態を保存し、制御を切り替えます。
IEEE 754浮動小数点規格における例外処理は、一般的に例外的な状況を指し、例外を「特定のオペランドに対する演算が、あらゆる妥当な用途に適した結果をもたらさない場合に発生する事象」と定義しています。この演算は、デフォルトの処理、または明示的に要求された場合は言語で定義された代替処理を呼び出すことによって、1つ以上の例外を通知する可能性があります。
デフォルトでは、IEEE 754 例外は再開可能であり、さまざまな例外に対して定義済みの値(たとえば、ゼロ除算例外の場合は無限大)を代入し、例外が発生したかどうかを後でチェックするためのステータス フラグを提供することで処理されます(IEEE 754 例外の処理の典型的な例については、C 言語リビジョンC99を参照してください)。ステータス フラグの使用によって可能になる例外処理スタイルには、まず高速で直接的な実装を使用して式を計算し、ステータス フラグをテストして失敗したかどうかを確認し、必要に応じて、より低速で数値的に堅牢な実装を呼び出すことが含まれます。[ 18 ]
IEEE 754 規格では、「トラッピング」という用語を使用して、例外的な状況でユーザーが提供する例外処理ルーチンを呼び出すことを指しており、これは規格のオプション機能です。この規格では、削除可能な特異点を簡潔に処理するために、デフォルト値以外の値を事前に置換してから再開する実装など、いくつかの使用シナリオを推奨しています。[ 18 ] [ 19 ] [ 20 ]
デフォルト値の事前置換後に再開するという IEEE 754 のデフォルトの例外処理動作は、数値例外によるプログラム制御の流れの変更に伴うリスクを回避します。たとえば、1996 年のCluster 宇宙船の打ち上げは、算術エラーで計算を中止するというAda の例外処理ポリシーが原因の一つとなって、壊滅的な爆発で終わりました。William Kahan氏は、IEEE 754 のデフォルトの例外処理動作であれば、これを防げたはずだと主張しています。[ 19 ]
コンピュータプログラミングでは、例外処理のためのプログラミング言語の仕組みがいくつか存在します。例外という用語は通常、例外的な状況に関する情報を格納するデータ構造を表すために使用されます。制御を移したり、例外を発生させたりする仕組みの1つは、 throwと呼ばれます。例外は、throw されたと言われます。実行はcatchに移されます。
ReactやVueなどのフロントエンドWeb開発フレームワークでは、エラーがユーザーインターフェース(UI)コンポーネント階層を伝播するエラー処理メカニズムが導入されており、これはコード実行時のコールスタックをエラーが伝播するのと類似しています。[ 21 ] [ 22 ]ここで、エラー境界メカニズムは、一般的なtry-catchメカニズムの類似物として機能します。したがって、コンポーネントは、子コンポーネントからのエラーが捕捉され処理され、親コンポーネントに伝播されないことを保証できます。
例えば、Vue.jsでは、コンポーネントはエラーをキャッチするために実装しますerrorCaptured
Vue.component('parent',{template: '<div><slot></slot></div>',errorCaptured:(err,vm,info)=>alert('An error occurred');})Vue.component('child',{template: '<div>{{cause_error()}}</div>'})
マークアップでこのように使用する場合:
<親> <子></子> </親>子コンポーネントによって発生したエラーは、親コンポーネントによって捕捉され、処理されます。[ 23 ]
ArrayIndexOutOfBoundsExceptionFileNotFoundExceptionFileNotFoundExceptionソフトウェア プログラマが使用する例外処理方法の中で最も一般的なのは、C や UNIX の一部として普及した「リターン コード」技術でしょう。
{{cite journal}}:ジャーナルを引用するには|journal=(ヘルプ)