行うことの違いは何ですか:
ptr = malloc (MAXELEMS * sizeof(char *));
また:
ptr = calloc (MAXELEMS, sizeof(char*));
calloc を malloc よりも優先して使用したり、その逆を使用したりするのは、どのような場合に適していますか?
calloc()
ゼロで初期化されたバッファを提供しますmalloc()
が、メモリは初期化されません。
大規模な割り当ての場合、calloc
主流の OS でのほとんどの実装では、既知のゼロ化されたページが OS から (たとえば POSIXmmap(MAP_ANONYMOUS)
または Windows経由VirtualAlloc
で) 取得されるため、それらをユーザー空間に書き込む必要はありません。これは、通常malloc
、OS からより多くのページを取得する方法でもあります。calloc
OSの保証を利用するだけです。
これは、calloc
メモリが引き続き「クリーン」で遅延割り当てされ、コピー オン ライトがシステム全体のゼロの共有物理ページにマップされることを意味します。(仮想メモリを備えたシステムを想定しています。)
一部のコンパイラでは、malloc + memset(0) を最適化して calloc にすることもできますが、メモリを0
.
書き込む前にメモリを読み取らない場合はmalloc
、OS から新しいページを取得する代わりに、内部の空きリストからダーティ メモリを (潜在的に) 与えることができるように使用します。(または、小さな割り当てのために空きリストのメモリ ブロックをゼロにする代わりに)。
の組み込み実装は、OS がない場合、またはプロセス間の情報漏えいを防ぐためにページをゼロにする派手なマルチユーザー OS でない場合、メモリをゼロにすることができますcalloc
。calloc
組み込み Linux の malloc could はmmap(MAP_UNINITIALIZED|MAP_ANONYMOUS)
、マルチユーザー システムでは安全でないため、一部の組み込みカーネルでのみ有効になっています。
あまり知られていない違いとして、Linux などの楽観的なメモリ割り当てを行うオペレーティング システムではmalloc
、プログラムが実際にアクセスするまでは、によって返されるポインタが実際のメモリによってサポートされないことがあります。
calloc
実際にメモリに触れます(メモリにゼロを書き込みます)。したがって、OSが実際のRAM(またはスワップ)で割り当てをバックアップしていることを確認できます。これが malloc よりも遅い理由でもあります (ゼロにする必要があるだけでなく、OS は他のプロセスをスワップアウトして適切なメモリ領域を見つける必要もあります)。
たとえば、malloc の動作に関する詳細な議論については、この SO の質問を参照してください。
見過ごされがちな利点の1つcalloc
は、整数のオーバーフローの脆弱性からユーザーを保護するのに役立つことです。比較:
size_t count = get_int32(file);
struct foo *bar = malloc(count * sizeof *bar);
対。
size_t count = get_int32(file);
struct foo *bar = calloc(count, sizeof *bar);
前者の場合、count
がより大きい場合、割り当てが小さくなり、後続のバッファオーバーフローが発生する可能性がありSIZE_MAX/sizeof *bar
ます。この場合、大きなオブジェクトを作成できないため、後者は自動的に失敗します。
もちろん、オーバーフローの可能性を単に無視する不適合な実装に注意する必要があるかもしれません...これがターゲットとするプラットフォームで懸念される場合は、とにかくオーバーフローを手動でテストする必要があります。
ドキュメントでは、メモリをゼロで初期化するだけのcalloc
ように見えます。malloc
これは主な違いではありません。のアイデアはcalloc
、メモリ割り当てのコピー オン ライト セマンティクスを抽象化することです。メモリを割り当てるとcalloc
、すべてがゼロに初期化された同じ物理ページにマップされます。割り当てられたメモリのページのいずれかが物理ページに書き込まれると、割り当てられます。これは、多くの場合、巨大なハッシュ テーブルを作成するために使用されます。たとえば、ハッシュの空の部分は余分なメモリ (ページ) によってサポートされていないためです。それらは、ゼロで初期化された単一のページを喜んで指し示します。このページは、プロセス間で共有することもできます。
仮想アドレスへの書き込みはページにマップされ、そのページがゼロページである場合、別の物理ページが割り当てられ、ゼロページがそこにコピーされ、制御フローがクライアントプロセスに返されます。これは、メモリ マップ ファイル、仮想メモリなどと同じように機能します。ページングを使用します。
このトピックに関する最適化の話の 1 つを次に示します。
割り当てられるメモリ ブロックのサイズに違いはありません。calloc
物理的なすべてゼロのビット パターンでメモリ ブロックを埋めるだけです。実際には、 で割り当てられたメモリ ブロックに配置されたオブジェクトは、calloc
あたかもリテラル0
で初期化されたかのように、初期0
値を持つと想定されることがよくあり0.0
ます。 、 等々。
ただし、ペダンティックな観点からは、calloc
(およびmemset(..., 0, ...)
) は、タイプ のオブジェクトを (ゼロで) 適切に初期化することのみが保証されますunsigned char
。それ以外はすべて、適切に初期化されることが保証されておらず、未定義の動作を引き起こす、いわゆるトラップ表現が含まれている可能性があります。つまり、前述のすべてゼロのビット パターン以外unsigned char
の型は、不正な値、トラップ表現を表す可能性があります。
その後、C99 標準の技術正誤表の 1 つで、すべての整数型に対して動作が定義されました (これは理にかなっています)。calloc
つまり、正式には、現在の C 言語では(および)を使用して整数型のみを初期化できますmemset(..., 0, ...)
。C言語の観点からすると、一般的なケースでそれを使用して他のものを初期化すると、未定義の動作が発生します。
実際にcalloc
は、私たち全員が知っているように機能します:)が、(上記を考慮して)それを使用するかどうかはあなた次第です。個人的には、完全に回避し、malloc
代わりに使用して、独自の初期化を実行することを好みます。
最後に、要素のサイズに要素の数を掛けてcalloc
、最終的なブロック サイズを内部的に計算するために必要なもう 1 つの重要な詳細があります。その際、calloc
算術オーバーフローの可能性に注意する必要があります。要求されたブロック サイズを正しく計算できない場合、割り当ては失敗します (null ポインター)。その間、あなたのmalloc
バージョンはオーバーフローを監視しようとしません。オーバーフローが発生した場合に備えて、「予測できない」量のメモリが割り当てられます。
Georg Hager のブログのcalloc() とゼロ ページによるベンチマークの楽しみの記事から
calloc() を使用してメモリを割り当てる場合、要求された量のメモリがすぐには割り当てられません。代わりに、メモリ ブロックに属するすべてのページは、MMU マジック (以下のリンク) によってすべてゼロを含む単一のページに接続されます。そのようなページが読み取り専用である場合 (これは、ベンチマークの元のバージョンの配列 b、c、および d に当てはまります)、データは単一のゼロ ページから提供され、もちろんキャッシュに収まります。メモリにバインドされたループ カーネルの場合はこれで終わりです。ページが (方法に関係なく) 書き込まれると、障害が発生し、「実際の」ページがマップされ、ゼロ ページがメモリにコピーされます。これは、copy-on-write と呼ばれる、よく知られた最適化アプローチです (C++ の講義で何度も教えたこともあります)。その後、
calloc
は一般malloc+memset
に 0です
malloc+memset
特に次のようなことをしている場合は、明示的に使用する方が一般的にわずかに優れています。
ptr=malloc(sizeof(Item));
memset(ptr, 0, sizeof(Item));
sizeof(Item)
コンパイル時にコンパイラに認識され、ほとんどの場合、コンパイラはそれをメモリをゼロにするための最良の命令に置き換えるため、これはより良い方法です。一方、 で発生している場合memset
、calloc
割り当てのパラメーター サイズはcalloc
コードでコンパイルされず、realmemset
が頻繁に呼び出されます。これには通常、サイクル ツー フィルではなく、長い境界までバイト単位でフィルアップするコードが含まれます。チャンクでメモリをsizeof(long)
増やし、最後に残りのスペースをバイトごとに埋めます。アロケーターがいくつかを呼び出すのに十分なほどスマートであっても、aligned_memset
それは依然として一般的なループになります。
注目すべき例外の 1 つは、メモリの非常に大きなチャンク (2 の累乗キロバイト) の malloc/calloc を実行している場合です。この場合、割り当てはカーネルから直接行われる可能性があります。OS カーネルは通常、セキュリティ上の理由から提供するすべてのメモリをゼロに設定するため、十分に賢い calloc は追加のゼロ化を行わずに単にそれを返す可能性があります。繰り返しますが、小さいことがわかっているものを単に割り当てている場合は、malloc+memset を使用した方がパフォーマンスが向上する可能性があります。
2つの違いがあります。
まず、引数の数です。malloc()
1つの引数(バイト単位で必要なメモリ)を取りますが、calloc()
2つの引数が必要です。
次に、割り当てられたメモリをゼロに初期化するmalloc()
間、割り当てられたメモリを初期化しません。calloc()
calloc()
メモリ領域を割り当てます。長さはそのパラメータの積になります。calloc
メモリをゼロで埋め、最初のバイトへのポインタを返します。十分なスペースが見つからない場合は、NULL
ポインターを返します。構文:ptr_var = calloc(no_of_blocks, size_of_each_block);
すなわちptr_var = calloc(n, s);
malloc()
REQUSTED SIZEのメモリの単一ブロックを割り当て、最初のバイトへのポインタを返します。必要な量のメモリを見つけることができない場合は、nullポインタを返します。構文:関数は、割り当てるバイト数である1つの引数を取りますが、ptr_var = malloc(Size_in_bytes);
関数は2つの引数を取ります。1つは要素の数で、もう1つはこれらの各要素に割り当てるバイト数です。また、割り当てられたスペースをゼロに初期化しますが、そうではありません。malloc()
calloc()
calloc()
malloc()
違い 1:
malloc()
通常、メモリ ブロックが割り当てられ、メモリ セグメントが初期化されます。
calloc()
メモリ ブロックを割り当て、すべてのメモリ ブロックを 0 に初期化します。
違い 2:
構文を考慮するとmalloc()
、引数は 1 つしか取りません。以下の例を考えてみましょう。
data_type ptr = (cast_type *)malloc( sizeof(data_type)*no_of_blocks );
例: int 型に 10 ブロックのメモリを割り当てたい場合、
int *ptr = (int *) malloc(sizeof(int) * 10 );
calloc()
構文を考慮すると、2 つの引数が必要になります。以下の例を考えてみましょう。
data_type ptr = (cast_type *)calloc(no_of_blocks, (sizeof(data_type)));
例: int 型に 10 ブロックのメモリを割り当て、それをすべて 0 に初期化する場合、
int *ptr = (int *) calloc(10, (sizeof(int)));
類似性:
malloc()
とはcalloc()
、型キャストされていない場合、デフォルトで void* を返します。
calloc()
ヘッダーで宣言されている関数には、関数<stdlib.h>
よりもいくつかの利点がありmalloc()
ます。
まだ言及されていない違い:サイズ制限
void *malloc(size_t size)
までしか割り当てられませんSIZE_MAX
。
void *calloc(size_t nmemb, size_t size);
約 を割り当てることができSIZE_MAX*SIZE_MAX
ます。
この機能は、線形アドレッシングを使用する多くのプラットフォームではあまり使用されません。このようなシステムは で制限calloc()
されnmemb * size <= SIZE_MAX
ます。
512 バイトのタイプが呼び出されdisk_sector
、コードが多数のセクターを使用することを検討してください。SIZE_MAX/sizeof disk_sector
ここで、コードはセクターまでしか使用できません。
size_t count = SIZE_MAX/sizeof disk_sector;
disk_sector *p = malloc(count * sizeof *p);
さらに大きな割り当てを可能にする次のことを検討してください。
size_t count = something_in_the_range(SIZE_MAX/sizeof disk_sector + 1, SIZE_MAX)
disk_sector *p = calloc(count, sizeof *p);
そのようなシステムがそのような大きな割り当てを提供できるかどうかは別の問題です. 今日のほとんどはそうではありません。ムーアの法則SIZE_MAX
を考えると、これは 2030 年頃に、100 GB のメモリ プールを備えた特定のメモリ モデルで発生すると思われます。SIZE_MAX == 4294967295
両方ともメモリmalloc
をcalloc
割り当てますが、calloc
すべてのビットをゼロに初期化しますが、そうではありmalloc
ません。
Calloc はmemset
、0 を指定した malloc + と同等であると言えます (ここで、memset はメモリの指定されたビットを 0 に設定します)。
したがって、ゼロへの初期化が必要ない場合は、malloc を使用した方が高速になる可能性があります。