答えはあなたの視点に依存します:
C ++標準で判断すると、未定義の動作が最初に発生するため、null参照を取得できません。未定義の動作が最初に発生した後、標準は何でも起こることを許可します。したがって、を記述した場合*(int*)0
、言語標準の観点からは、nullポインターを逆参照しているので、未定義の動作が既にあります。プログラムの残りの部分は関係ありません。この式が実行されると、ゲームから外れます。
ただし、実際には、nullポインターはnullポインターから簡単に作成でき、null参照の背後にある値に実際にアクセスしようとするまで気付かないでしょう。あなたの例は少し単純すぎるかもしれません。良い最適化コンパイラは未定義の振る舞いを見て、それに依存するものを単に最適化するだけです(null参照は作成されず、最適化されます)。
しかし、その最適化はコンパイラーに依存して未定義の動作を証明することに依存しますが、これは不可能かもしれません。ファイル内のこの単純な関数を考えてみましょうconverter.cpp
:
int& toReference(int* pointer) {
return *pointer;
}
コンパイラーがこの関数を見つけたとき、ポインターがNULLポインターであるかどうかはわかりません。したがって、ポインタを対応する参照に変換するコードを生成するだけです。(ところで:ポインターと参照はアセンブラーでまったく同じ獣であるため、これは何もしません。)ここuser.cpp
で、コードを含む別のファイルがある場合
#include "converter.h"
void foo() {
int& nullRef = toReference(nullptr);
cout << nullRef; //crash happens here
}
コンパイラはそれを知りません toReference()
、渡されたポインターを逆参照するを、実際にはnull参照である有効な参照を返すと想定します。呼び出しは成功しますが、参照を使用しようとすると、プログラムがクラッシュします。うまくいけば。この標準では、ピンクの象の外観を含め、あらゆることが起こります。
なぜこれが関係しているのかと尋ねると、結局のところ、未定義の動作がすでに内部でトリガーされていましたtoReference()
。答えはデバッグです。Nullポインタと同じように、Null参照が伝播および増殖する可能性があります。null参照が存在する可能性があることに気付いておらず、それらの作成を回避する方法を学ぶ場合、プレーンな古いint
メンバーを読み取ろうとしているだけでメンバー関数がクラッシュしているように見える理由を理解するのにかなりの時間を費やす可能性があります(答え:インスタンスメンバーの呼び出しでnull参照があったためthis
、nullポインターもメンバーであり、メンバーはアドレス8)として配置されるように計算されます。
では、null参照をチェックするのはどうでしょうか?あなたはラインを与えました
if( & nullReference == 0 ) // null reference
あなたの質問で。まあ、それは機能しません:標準によると、nullポインターを逆参照すると、未定義の動作が発生し、nullポインターを逆参照せずにnull参照を作成することはできないため、null参照は未定義の動作の領域内にのみ存在します。コンパイラーは、未定義の動作をトリガーしていないと想定する場合があるため、null参照などのものが存在しないと想定できます(null参照を生成するコードがすぐに出力される場合でも!)。そのため、if()
条件を確認し、それが真ではないと結論付け、if()
ステートメント全体を破棄します。リンク時の最適化の導入により、堅牢な方法でnull参照をチェックすることは明らかに不可能になりました。
TL; DR:
null参照はやや恐ろしい存在です。
それらの存在は(=標準では)不可能であるように見えます
が、存在します(=生成されたマシンコードによる)が、
存在するかどうかはわかりません(=試行は最適化されます)
とにかく、気づかないうちに殺される可能性があります(=あなたのプログラムは奇妙なポイントで、またはそれより悪いところでクラッシュします。
あなたの唯一の望みは、それらが存在しないことです(=それらを作成しないようにプログラムを記述してください)。
それがあなたを悩ませることにならないことを願っています!