プラットフォームごとに異なるかもしれませんが、
gccを使用してコンパイルし、以下のコードを実行すると、ubuntu 11.10で毎回0が取得されます。
#include <stdio.h>
#include <stdlib.h>
int main()
{
double *a = (double*) malloc(sizeof(double)*100)
printf("%f", *a);
}
Callocがあってもmallocがこのように動作するのはなぜですか?
場合によっては値を0に初期化するだけでも、不要なパフォーマンスオーバーヘッドがあることを意味しませんか?
編集:ああ、私の前の例は初期化ではありませんでしたが、たまたま「フレッシュ」ブロックを使用していました。
私が正確に探していたのは、大きなブロックを割り当てるときに初期化する理由でした:
int main()
{
int *a = (int*) malloc(sizeof(int)*200000);
a[10] = 3;
printf("%d", *(a+10));
free(a);
a = (double*) malloc(sizeof(double)*200000);
printf("%d", *(a+10));
}
OUTPUT: 3
0 (initialized)
しかし、mallocingにはセキュリティ上の理由があることを指摘してくれてありがとう! (それについて考えなかった)。新しいブロックまたは大きなブロックを割り当てるときは、必ずゼロに初期化する必要があります。
短い答え:
そうではなく、あなたの場合はたまたまゼロになっています。
(データがゼロであることはテストケースにも表示されません。1つの要素がゼロの場合にのみ表示されます。)
ロングアンサー:
malloc()
を呼び出すと、次の2つのいずれかが発生します。
前者の場合、メモリには以前の割り当てから残ったデータが含まれます。したがって、ゼロにはなりません。これは、小さな割り当てを実行する場合の通常のケースです。
2番目の場合、メモリはOSからのものです。これは、プログラムのメモリが不足した場合、または非常に大きな割り当てを要求している場合に発生します。 (あなたの例の場合のように)
キャッチは次のとおりです。OSからのメモリは、security理由のためにゼロになります。*
OSがメモリを提供する場合、別のプロセスから解放されている可能性があります。そのため、メモリにパスワードなどの機密情報を含めることができます。そのため、そのようなデータを読み取らないようにするため、OSはデータを提供する前にゼロにします。
* C標準ではこれについて何も述べられていないことに注意してください。これは厳密にOSの動作です。したがって、このゼロ化は、セキュリティが問題にならないシステム上に存在する場合と存在しない場合があります。
これにパフォーマンスの背景をさらに与えるには:
@Rとしてコメントで言及されていますが、このゼロ化は常に calloc()
+ malloc()
の代わりにmemset()
を使用する必要がある理由です。 calloc()
はこの事実を利用して、別個のmemset()
を回避できます。
一方、このゼロ化はパフォーマンスのボトルネックになる場合があります。一部の数値アプリケーション( out-of-place FFT など)では、スクラッチメモリの巨大なチャンクを割り当てる必要があります。アルゴリズムを実行するために使用し、それを解放します。
これらの場合、ゼロ化は不要であり、純粋なオーバーヘッドになります。
私が見た中で最も極端な例は、48 GBのスクラッチバッファーを使用した70秒の操作で20秒のゼロ化オーバーヘッドです。 (およそ30%のオーバーヘッド。) (許可:マシンのメモリ帯域幅が不足していました。)
明らかな解決策は、メモリを手動で単純に再利用することです。しかし、それはしばしば確立されたインターフェースを突破する必要があります。 (特に、それがライブラリルーチンの一部である場合)
OSは通常、プロセスに送信する新しいメモリページをクリアして、古いプロセスのデータを参照できないようにします。つまり、変数(またはmallocの何か)を最初に初期化すると、多くの場合0になりますが、そのメモリを再利用する(たとえば、そのメモリを解放してから再度mallocする)と、すべてのベットがオフになります。
この矛盾は、初期化されていない変数がバグを見つけるのが非常に難しい理由です。
不要なパフォーマンスオーバーヘッドについては、未指定の動作を回避することがおそらく重要です。この場合に得られる小さなパフォーマンスブーストは、誰かがコードを少し変更した場合(以前の仮定を破る)または別のシステムに移植した場合(仮定が無効である可能性がある場合)に対処しなければならないバグを見つけるのを補償しませんそもそも)。
malloc()
がゼロに初期化されると仮定するのはなぜですか? malloc()
への最初の呼び出しは、OSからメモリのページを割り当てるsbrk
またはmmap
システム呼び出しへの呼び出しになります。 OSは、セキュリティ上の理由からゼロで初期化されたメモリを提供する義務があります(そうでない場合、他のプロセスからのデータが表示されます!)。だからあなたはそこにあると思うかもしれません-OSはページをゼロにする時間を無駄にします。しかし、違います! Linuxには、「ゼロページ」と呼ばれる特別なシステム全体のシングルトンページがあり、そのページはコピーオンライトとしてマップされます。つまり、実際にそのページに書き込む場合のみ、OSは別のページを割り当て、それを初期化します。だから、これがパフォーマンスに関するあなたの質問に答えることを願っています。メモリページングモデルでは、同じページの複数のマッピング機能に加えて、最初の書き込みが発生した場合にケースを処理する機能をサポートすることにより、メモリの使用を多少遅延させることができます。
free()
を呼び出すと、glibc
アロケーターは領域をその空きリストに戻します。また、malloc()
が再度呼び出されると、同じ領域を取得できますが、前のデータで。最終的に、free()
は、システムコールを再度呼び出して、OSにメモリを返す可能性があります。
glibc
manページ on malloc()
には、メモリがクリアされないことが厳密に記載されているため、APIの「契約」では、それを想定できないことに注意してください。クリアされます。元の抜粋は次のとおりです。
malloc()はsizeバイトを割り当て、割り当てられたメモリへのポインタを返します。
メモリはクリアされません。サイズが0の場合、malloc()はNULL、またはfree()に後で正常に渡すことができる一意のポインター値のいずれかを返します。
必要に応じて、パフォーマンスやその他の副作用が心配な場合は、そのドキュメントの詳細を読むことができます。
2つの同一の割り当てを含むように例を変更しました。これで、malloc
がメモリをゼロで初期化しないことが簡単にわかります。
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
{
double *a = malloc(sizeof(double)*100);
*a = 100;
printf("%f\n", *a);
free(a);
}
{
double *a = malloc(sizeof(double)*100);
printf("%f\n", *a);
free(a);
}
return 0;
}
Gcc 4.3.4での出力
100.000000
100.000000
gnu.org から:
この実装では、非常に大きなブロック(ページよりもはるかに大きい)がmmap(匿名または/ dev/zero経由)で割り当てられます。
規格は、malloc()
が値をゼロに初期化することを指示していません。プラットフォームでゼロに設定されたり、その値を読み込んだ特定の瞬間にゼロになった可能性があります。
コードは、malloc
がメモリを0に初期化することを示していません。これは、プログラムの開始前にオペレーティングシステムで実行できます。シッチを見るには、メモリに別の値を書き込んで解放し、mallocを再度呼び出します。おそらく同じアドレスを取得しますが、これを確認する必要があります。その場合は、内容を確認してください。知らせて下さい!
確実に初期化されていることを知っていますか? malloc()によって返される領域の先頭に0が頻繁にある可能性はありますか?
Never everカウントanyコンパイラーは、メモリーを初期化するコードを生成します。 mallocは、nバイトのメモリsomeplaceへのポインタを返すだけです。
メモリの内容が重要な場合は、自分で初期化します。