私はC ++を学んでおり、nullを理解するのに苦労しています。特に、私が読んだチュートリアルでは「nullチェック」の実行に言及していますが、それが何を意味するのか、なぜ必要なのかはわかりません。
- ヌルとは正確には何ですか?
- 「nullをチェックする」とはどういう意味ですか?
- 常にヌルをチェックする必要がありますか?
どんなコード例でも大歓迎です。
私はC ++を学んでおり、nullを理解するのに苦労しています。特に、私が読んだチュートリアルでは「nullチェック」の実行に言及していますが、それが何を意味するのか、なぜ必要なのかはわかりません。
どんなコード例でも大歓迎です。
回答:
CおよびC ++では、ポインターは本質的に安全ではありません。つまり、ポインターを間接参照する場合は、ポインターが有効な場所を指すようにするのはユーザーの責任です。これは、「手動メモリ管理」の目的の一部です(Java、PHP、または.NETランタイムなどの言語で実装された自動メモリ管理スキームとは異なり、かなりの努力なしに無効な参照を作成できません)。
多くのエラーをキャッチする一般的な解決策は、NULL
(または正しいC ++で0
)何も指し示していないすべてのポインターを設定し、ポインターにアクセスする前にそれをチェックすることです。具体的には、すべてのポインターをNULLに初期化して(宣言時にポイントするものがない限り)、delete
ユーザーまたはfree()
それらのときにNULLに設定するのが一般的です(その後すぐにスコープ外に出ない限り)。例(Cですが、有効なC ++):
void fill_foo(int* foo) {
*foo = 23; // this will crash and burn if foo is NULL
}
より良いバージョン:
void fill_foo(int* foo) {
if (!foo) { // this is the NULL check
printf("This is wrong\n");
return;
}
*foo = 23;
}
nullチェックを行わない場合、この関数にNULLポインターを渡すとセグメンテーション違反が発生し、何もすることはできません。OSは単にプロセスを強制終了し、場合によってはコアダンプするか、クラッシュレポートダイアログを表示します。ヌルチェックを設定すると、適切なエラー処理を実行して適切に回復できます。問題を自分で修正し、現在の操作を中止し、ログエントリを書き込み、ユーザーに通知します。
他の答えはあなたの正確な質問をほとんどカバーしていました。nullチェックは、受け取ったポインターが実際に型(オブジェクト、プリミティブなど)の有効なインスタンスを指していることを確認するために行われます。
ただし、ここに独自のアドバイスを追加します。nullチェックを避けます。:) Nullチェック(およびその他の形式の防御プログラミング)はコードを乱雑にし、実際に他のエラー処理手法よりもエラーを起こしやすくします。
オブジェクトポインターに関しては、Null Objectパターンを使用するのが私のお気に入りのテクニックです。つまり、nullの代わりに(ポインタ-またはそれ以上の参照)空の配列またはリストを返すか、nullの代わりに空の文字列( "")を返すか、文字列 "0"(または "nothingコンテキストで)整数に解析されると予想される場所。
おまけとして、1965年にCAR HoareがAlgol W言語用に(最初に)実装したヌルポインターについて、あなたが知らなかったかもしれないちょっとしたものがあります。
私はそれを10億ドルの間違いと呼んでいます。それは1965年のヌル参照の発明でした。当時、私はオブジェクト指向言語(ALGOL W)での参照のための最初の包括的な型システムを設計していました。私の目標は、コンパイラによって自動的に実行されるチェックを使用して、参照のすべての使用が絶対に安全であることを保証することでした。しかし、実装が非常に簡単だったという理由だけで、null参照を挿入する誘惑に抵抗することはできませんでした。これにより、無数のエラー、脆弱性、およびシステムクラッシュが発生し、過去40年間で数十億ドルの痛みと損害を引き起こした可能性があります。
nullポインター値は、明確に定義された「nowhere」を表します。それは無効、他のポインタ値に等しくない比較することが保証されているポインタ値。nullポインターを逆参照しようとすると、未定義の動作が発生し、通常はランタイムエラーが発生するため、ポインターを逆参照する前にNULLでないことを確認する必要があります。多くのCおよびC ++ライブラリ関数は、エラー状態を示すヌルポインターを返します。たとえば、ライブラリ関数malloc
は、要求されたバイト数を割り当てることができない場合、nullポインター値を返し、そのポインターを使用してメモリにアクセスしようとすると、(通常)ランタイムエラーが発生します。
int *p = malloc(sizeof *p * N);
p[0] = ...; // this will (usually) blow up if malloc returned NULL
したがって、NULLに対するmalloc
値をチェックして、呼び出しが成功したことを確認する必要がありp
ます。
int *p = malloc(sizeof *p * N);
if (p != NULL) // or just if (p)
p[0] = ...;
さて、靴下をちょっと待ってください、これは少しでこぼこになります。
NULLポインター値とNULLポインター定数があり、2つは必ずしも同じではありません。nullポインター値は、「nowhere」を表すために基礎となるアーキテクチャが使用する値です。この値は、0x00000000、0xFFFFFFFF、0xDEADBEEF、またはまったく異なるものです。NULLポインター値が常に0であると想定しないでください。
nullポインター 定数 OTOHは、常に0値の整数式です。限り、あなたのようにソースコードが関係している、0(または任意の積分式が0に評価される)は、ヌル・ポインタを表します。CとC ++は、NULLマクロをNULLポインター定数として定義します。コードがコンパイルされると、ヌルポインター定数は、生成されたマシンコード内の適切なヌルポインター値に置き換えられます。
また、NULLは多くの無効なポインター値の1つにすぎないことに注意してください。次のように、明示的に初期化せずに自動ポインタ変数を宣言する場合
int *p;
変数に最初に格納される値は不定であり、有効またはアクセス可能なメモリアドレスに対応しない場合があります。残念ながら、使用する前にNULL以外のポインター値が有効かどうかを確認する(移植可能な)方法はありません。したがって、ポインターを扱う場合は、通常、宣言時に明示的にNULLに初期化し、積極的に何かを指し示していないときにNULLに設定することをお勧めします。
これはC ++ではなくCの問題であることに注意してください。慣用的なC ++はあまりポインターを使用すべきではありません。
いくつかの方法があり、基本的にすべて同じことを行います。
int * foo = NULL; // NULLではなく0x00または0または0Lに設定される場合があります
nullチェック(ポインターがnullかどうかをチェック)、バージョンA
if(foo == NULL)
nullチェック、バージョンB
if(!foo)// NULLは0として定義されているため、!fooはNULLポインターから値を返します
nullチェック、バージョンC
if(foo == 0)
3つのうち、最初のチェックを使用することを好みます。これは、将来の開発者にあなたがチェックしようとしていたことを明示的に伝えるためです。
あなたはしません。C ++でポインターを使用する唯一の理由は、nullポインターの存在が明示的に必要だからです。それ以外の場合は、参照を使用できます。これは、セマンティックに使いやすく、非nullを保証します。
export
)と、すべてのC ++ 03ライブラリ機能、 TR1 、および C ++ 11のかなりの部分が含まれます。