メモリをゼロにする (つまりcalloc()
を超えるmalloc()
) 利点は何ですか? とにかく値を別のものに変更しませんか?
7 に答える
2 つの陣営があります。1 つは、宣言時に変数を初期化すると、バグの発見に役立つというものです。このキャンプの人々は、宣言するすべてが初期化されていることを確認します。ポインターをNULL
、int
s を 0 などに初期化します。すべてが確定的でありNULL
、デバッガーでポインターが表示されると、正しく設定されていないことがすぐにわかります。NULL
また、実稼働環境で不可解にクラッシュするのではなく、-pointerの逆参照が原因でテスト中にプログラムがクラッシュするのにも役立ちます。
もう一方の陣営は、宣言時に変数を初期化すると、デバッグが難しくなると述べています。これは、コンパイラが「設定せずに使用された」変数について警告できないためです。
私の個人的な好みを言わずに1 : もしあなたが最初のキャンプに属しているなら、calloc()
代わりにmalloc()
. あなたが 2 番目の陣営に属している場合 (そうであるように思われます)、あなたmalloc()
はcalloc()
.
現在、2 つの例外があります。
- 「すべてを初期化する」陣営に属している場合は、
calloc()
浮動malloc()
小数点数またはポインターを初期化していて、すべてのビットがゼロであるとは限らないことを知っているため、そうではありません0
。または、余分なオーバーヘッドは必要ありません。 - 「必要なときに設定する」陣営に属している場合は、
calloc()
データを割り当てているときにすべてゼロにしたい場合があります。たとえば、動的に割り当てられたn
データの行ごとの合計を計算する場合。m
int
1 SO で多くの質問に対する私の回答を見て、私がどのキャンプに属しているかを確認できます :-)。
- すでに存在する値を知ることで、プログラマーはいくつかの近道を取り、特定の最適化を行うことができます。ほとんどの場合、
calloc
ポインターを使用して構造体を ing します。ポインターは NULL に初期化されます。 - プログラマーが割り当てで何かを初期化するのを忘れた場合はどうなりますか? ランダムなものの代わりに、ゼロは優れたデフォルト値です。
interrupt 3
私がかなり前に取り組んだリアルタイム プロセス制御システムでは、パワーオン ロジックですべての RAM を 8086 の命令である 0xCC に初期化することにしました。これにより、何らかの方法で初期化されていないメモリが実行された場合、プロセッサがモニター (プリミティブ デバッガー) に入ります。(役に立たないことに、8086 は命令であるため、ゼロを含むメモリを陽気に実行しますadd [bx+si],al
。32 ビット モードでさえ、それらは命令になりadd [ax],al
ます。)
ランナウェイ プログラムが見つかったかどうかは覚えていませんが、0xCC に対応する値はさまざまな値でした: 52,428 (符号なし 16 ビット)、-19,660 (符号付き 16 ビット)、-107374176 (32 ビット浮動小数点)、および -9.25596313493 e+61 (64 ビット浮動小数点) は、予期しない多くの場所でポップアップしました。また、文字が 7 ビット ASCII であることを想定している一部のコード (つまり、バグ) は、0xCC を処理しようとしたときに、その存在を警告しました。
カウントソートの実装を書きたい、または深さ優先でグラフを検索し、訪問した頂点を追跡したいとします。アルゴリズムの実行時にメモリを更新します (値を一度だけ割り当てるのではなく)。最初にゼロに初期化する必要があります。を持っていない場合はcalloc
、手動で調べて、アルゴリズムの最初にゼロに初期化する必要があります。calloc
これをより効率的に実行できる可能性があります。
割り当てているものはすべてゼロに初期化されることを知っておくとよいでしょう。多くのバグは、初期化されていないメモリを使用するコードから発生しています。さらに、構造体/クラスの一部のデフォルト値はゼロで問題ない場合があるため、malloc の後にすべての値を変更する必要はありません。
たとえば、malloc を使用して、いくつかのポインターを含む構造体を割り当てます。NULL チェックは、NULL に設定されていない限り、常に機能するとは限りません。calloc を使用すると、ポインター値の追加の初期化手順を実行する必要がなくなります。
変数を初期化する利点に加えて、callocはバグの追跡にも役立ちます。
適切に初期化せずに割り当てられたメモリの一部を誤って使用した場合、アプリケーションは常に同じように失敗します。たとえば、nullポインタからのアクセス違反があります。mallocでは、メモリにランダムな値があり、これによりプログラムがランダムに失敗する可能性があります。
ランダムな障害を追跡することは非常に困難であり、callocはそれらを回避するのに役立ちます。
誰もパフォーマンスの側面に触れていないので、私はしなければならないと思います. 「万が一に備えて」統合された memset を使用して非常に高速なプログラム malloc を作成する必要がある場合は、適切な方法ではありません。memset がどれほど高速であっても、常に遅すぎます。ベクトルまたは配列を初期化する必要がある場合があるため、本当の問題はクロック サイクルを制御することです (つまり、クロック サイクルを無駄にしないことです)。「誤ってパフォーマンスをあきらめてはならない」という言葉を聞いたことがありますが、これは、パフォーマンスの観点から、何らかの方法でコードを実装することを選択した理由 (長所と短所が何であり、それらがどのように比較されるか) を常に知っておく必要があることを意味します。特定のケースでは互いに)。
文字列で埋められるバッファがある場合、文字列が埋められる前にバッファを初期化するのは「良い」かもしれませんが、ほとんどの人はそれがクロックサイクルの完全な無駄であることに同意します。新しい str* 関数を作成している場合、デバッグの目的で、通常は表示されないはずの値でバッファを埋めたいと思うかもしれませんが、これは配布時に削除されます。
他の人が述べたように、初期化されていない変数がアクセスされている場合、コンパイラは警告を発します。
まず第一に、少なくとも標準 C に従いたい場合は、ポインターを calloc できません。
第二に、メンバーをすべてゼロで上書きすると、バグがマスクされるだけです。0xCDCDCDCD など、常にクラッシュするものにメモリを初期化するデバッグ バージョンの malloc を使用することをお勧めします。
その後、Access の違反が表示されたときに、問題がすぐにわかります。解放された後にメモリに触れる人が予期しない驚きを得るために、メモリを異なるパターンでホイップするデバッグフリー機能があることも有益です。
組み込みシステムで作業している場合、「確実に」するためだけに呼び出しを行うことは通常、オプションではありません。通常、一度に割り当ててデータを入力するため、メモリに二重に触れているだけでcallocします。