Undefined値
![]() | この項目「Undefined値」は翻訳されたばかりのものです。不自然あるいは曖昧な表現などが含まれる可能性があり、このままでは読みづらいかもしれません。(原文:en:Undefined value) 修正、加筆に協力し、現在の表現をより自然な表現にして下さる方を求めています。ノートページや履歴も参照してください。(2022年12月) |
![]() |
コンピューティング、特にプログラミングにおける未定義値(英: undefined value、アンデファインド値またはアンディファインド値)とは、(構文的には正しくても)式が正しい値を持たない状態・状況である。空の文字列、ブーリアン型の「偽値」(false value)、またはその他の「空の値」(empty value) とは異なるため、混同してはならない。状況によっては、未定義の値を評価すると例外または未定義の動作が発生する場合があるが、一部のプログラミング言語では、通常の予測可能なプログラム実行過程の中で未定義の値が発生する可能性がある。
動的型付けされた言語は通常、可能な場合は未定義の値を明示的に扱う。たとえば、 Perlにはそのような値を変数に「割り当てる」ことができるundef
演算子が存在する。他の型システムでは、未定義の値はプログラムの失敗を意味する可能性がある。 Null許容型は中間的なアプローチを提供する。
取り扱い
[編集]引数が定義域の範囲外だった場合、部分関数の値は未定義となる。これはゼロ除算、負数に対する平方根や対数など、多数の算術的な事例を含む。別のよくある事例としては、範囲外のインデックスで配列にアクセスすることで発生する(連想配列において、対応するキーが含まれていない場合の値と同様)。このような状況を実際に処理するには、さまざまな方法がある。
予約値
[編集]未定義の値を適切に処理する必要があるアプリケーションでは、通常の値と区別できる特別なnull値を予約するのが一般的である。これは、かつては未定義だったケースを表すための定義済みの値を作成することにより、問題を解決する。
動的に型付けされた言語では、初期化されていない変数がデフォルトでnullになることがあるが、静的に型付けされた値では明確に定義されたnullと初期化されていない値を区別する[1]。
例外処理
[編集]一部のプログラミング言語には、値を返すのに失敗した場合に対処するための例外処理の概念がある。関数は定義された方法で実行されるが、値は返さないため、特別な返却値を捻出する必要がなくなる。
このバリエーションとして、シグナル処理がある。これはオペレーティングシステムレベルで実行され、プログラミング言語には統合されていない。シグナルハンドラーは、計算の一部を終了するなど、いくつかの形式の回復を試みることができるが、完全に統合された例外処理ほどの柔軟性はない。
復帰しない関数
[編集]呼び出し元に決して返ってくることのない(non-return)関数は、未定義の値を持つ。その値は決して観測されることがないからである。このような関数には、形式上、値を持たないボトム型が割り当てられている。その例は2つのカテゴリに分類される。
- 無限ループする関数。これは、故意に、または決して見つからないものを検索した結果として発生する可能性がある。
exit
システムコールなど、計算を終了する関数。プログラム内からは、これは前のケースと区別できないが、プログラムの呼び出し元に違いが生じる。
未定義の動作
[編集]前述の未定義の値をすべて処理するには、未定義性 (undefinedness) を検出する必要がある。つまり、呼び出された関数が、通常の結果を返すことができないと判断し、呼び出し元に通知するためのアクションを実行する。対極的に、未定義の動作 (米: undefined behavior, 英: undefined behaviour) は、範囲外の引数を使用して関数を呼び出さないようにするという責任を呼び出し元に課す。何が起こりうるかということに関する制限はない。検出のしやすいクラッシュを引き起こすのがせいぜいだが、最悪の場合、一見無関係な計算における微妙なエラーを引き起こす。
また、未定義の値は、ハードウェアで特に頻繁に発生する。配線が有用な情報を伝達していない場合でも、配線は存在し、ある程度の電圧レベルを持つ。
データバッファが提供されているが完全に満たされていない場合、ソフトウェアでも同じ状況が発生する。たとえば、Cライブラリのstrftime
関数は、タイムスタンプを人間が読める(文字列の)形式に変換して、提供された出力バッファに格納するが、出力バッファが結果を保持するのに十分な大きさでない場合、戻り値ではエラーを示す整数値0が返され、バッファの内容は未定義になる[2]。
逆の例として、POSIXのopen
システムコールがある。これは、ファイル名、いくつかのフラグ、およびファイルモードの3つの引数を取るが、ファイルモードは、フラグにO_CREAT
が含まれている場合にのみ使用される(可変長引数であり、指定は任意[3][4])。フラグからO_CREAT
が除外されている場合、2つの引数を受け取る形式のopen
を使用する(ファイルモードには未定義の値を与える)のが一般的である。
このような未定義値を制限された方法で扱うと便利な場合がある。未定義の値が後で無視された場合でも、全体的な計算は明確に定義できる。
この例として、C言語では、ポインターを整数に変換できるが、その整数の数値は未定義[要出典]である。それでも、デバッグ、2つのポインターの同等性の比較、またはXORリンクリストの作成に役立つ場合がある。
未定義の値を安全に処理することは、事後に競合状態を検出する楽観的同時実行制御システムで重要になる。たとえば、 seqlockで保護されている共有変数を読み取ると、競合状態が発生したと判断する前に未定義の値が生成される。その後、未定義のデータを破棄し、操作を再試行する。これにより、未定義の値に対して実行された操作が本格的な未定義の動作を生成しない限り、定義された結果が生成される。
表記
[編集]計算可能性理論では、式の未定義性はexpr↑で表され、定義性はexpr↓で表される。
関連項目
[編集]参考文献
[編集]- ^ “C# Nullable Numeric Data Types”. C# Fundamentals tutorial (2006年10月1日). 2010年3月27日閲覧。
- ^ strftime - cppreference.com
- ^ open | The Open Group Base Specifications Issue 7 - IEEE Std 1003.1-2008
- ^ open(2) - Linux manual page