web-dev-qa-db-ja.com

初期化時にpointer = NULLを呼び出す必要がありますか?

特定の構造体へのポインターを作成するとき、それをNULLに設定し、それを割り当ててから使用する必要がありますか?なぜ?

13
user1051003

いいえ、(言語に関する限り)ポインタ変数を宣言するときに何かに初期化する必要はありません。したがって、

T* ptr;

は、値が不確定なptrという名前の変数を導入する有効な宣言です。最初に何かを割り当てたり、特定の値に設定したりせずに、特定の方法で変数を使用することもできます。

func(&ptr);
8
eq-

いいえ、NULLに設定する必要はありませんが、新しいポインタに値を与えるため、良い習慣だと考える人もいます明示的何も指していません(まだ)。

ポインタを作成してからすぐに別の値をポインタに割り当てる場合、それをNULLに設定してもあまり価値はありません。

ただし、ポイントしていたメモリを解放した後、ポインタをNULLに設定することをお勧めします。

12
Levon

C標準によれば、自動ストレージ変数を初期化しないと、その値は不確定になります。

ポインタの値が不確定である場合は常に、ポインタをNULLに設定することを強くお勧めします。値が不確定なポインターの使用法は未定義の振る舞いです。これは、Cプログラミングに慣れていて、高級言語を使用している場合は、正しく理解できない可能性がある概念です。

未定義の振る舞いは何かが起こる可能性があることを意味しますそしてこれはC標準の一部です。Cの哲学はプログラマーに彼の過ちから彼を保護するのではなく、物事を制御する責任を負わせるからです。

コード内の未定義の動作を回避する必要があります。動作が発生する可能性がある場合、デバッグは非常に困難です。コンパイラによって検出されることはなく、テストは常に合格する可能性があり、修正に非常に費用がかかるまでバグは気付かれません。

あなたがこのようなことをするなら:

char *p;
if(!p) puts("Pointer is NULL");

そのifコントロールがtrueかfalseかは実際にはわかりません。これは、変数を宣言してから、空間と時間の非常に遠い場所で使用する可能性がある大規模なプログラムでは非常に危険です。

解放されたメモリを参照する場合も同じ概念です。

free(p);
printf("%s\n", p);
p = q;

最後の2つのステートメントで何が得られるかは実際にはわかりません。あなたはそれを適切にテストすることができません、あなたはあなたの結果を確信することができません。解放されたメモリは変更されていてもリサイクルされるため、コードが機能しているように見える場合があります。メモリを上書きしたり、同じメモリを共有する他の変数を破損したりする可能性もあります。そのため、遅かれ早かれ明らかになり、デバッグが非常に困難になる可能性のあるバグがまだあります。

ポインタをNULLに設定すると、これらの危険な状況から身を守ることができます。テスト中、プログラムは決定論的で予測可能な動作をし、高速かつ安価に失敗します。セグメンテーション違反が発生し、バグをキャッチし、バグを修正します。清潔でシンプル:

#include <stdio.h>
#include <stdlib.h>

int main(){
  char* q;
  if(!q) puts("This may or may not happen, who knows");
  q = malloc(10);
  free(q);
  printf("%s\n", q); // this is probably don't going to crash, but you still have a bug

  char* p = NULL;
  if(!p) puts("This is always going to happen");
  p = malloc(10);
  free(p);
  p = NULL;
  printf("%s\n", p); // this is always going to crash

  return 0;
}

したがって、特に大規模なプログラムでポインタを初期化するときは、ポインタを明示的に初期化するか、NULLに設定する必要があります。あなたは彼らに不確定な価値を与えてほしくない、決して。私はあなたが何をしているのかを知らない限りと言うべきです、しかし私は決して代わりに。

3
gtugnolo

いいえ、初期化はnullポインタに対するものでなければならないことを忘れないでください。現代のCで非常に便利なイディオムは、最初の使用時に変数を宣言することです。

T * ptr = malloc(sizeof *ptr);

これにより、型を覚えたり、変数がすでに初期化されているかどうかを覚えたりする手間を省くことができます。後でどこで初期化されるかがわからない場合(または初期化される場合でも)にのみ、nullポインターに確実に初期化する必要があります。

したがって、経験則として、変数は常に適切な値に初期化してください。 「適切な0 for the type "は、手元に良いものがない場合は常に良い選択です。すべてのタイプで、Cはそのように作られているので機能します。

ほとんどの場合、変数を初期化しないことは時期尚早の最適化です。そこに実際のパフォーマンスのボトルネックがあることがわかった場合にのみ、変数を調べてください。特に、初期値を使用する前に同じ関数内に割り当てがある場合、最新のコンパイラーは初期化を最適化します。最初にプログラムの正しさを考えてください。

2
Jens Gustedt

あなたがそれをしばらくぶら下げたくないのでなければ、あなたはそうする必要はありません。あなたはあなたを知っていますすべきです解放した後(防御的なスタイル)。

NULLに初期化する必要はありません。すぐに割り当てる場合は、スキップできます。

Atは、次の例のようにエラー処理に役立ちます。この場合、pの割り当て後に最後まで中断すると、qは初期化されません。

int func(void)
{
    char *p;
    char *q;

    p = malloc(100);
    if (!p)
        goto end;

    q = malloc(100);
    if (!q)
        goto end;

    /* Do something */

end:
    free(p);
    free(q);

    return 0;
}

また、これは私の個人的な好みですが、構造体は常にcallocで割り当てます。文字列は、mallocで初期化せずに割り当てることができます

0
eyalm