mallocとcallocの違いは?


780

行うことの違いは何ですか:

ptr = (char **) malloc (MAXELEMS * sizeof(char *));

または:

ptr = (char **) calloc (MAXELEMS, sizeof(char*));

mallocよりもcallocを使用したり、その逆を使用したりするのはどのような場合に適していますか?



8
:Cでは、あなたは上記のより一般的のように書くことができるptr = calloc(MAXELEMS, sizeof(*ptr));
chqrlie


2
@ddddavideeネットでたくさんの答えに不満があった後、私もそのブログを見つけました。ナサニエルJ.スミスは、彼の分析のために100ポイント以上のSOポイントに値します。
ライフバランス

回答:


851

calloc()malloc()初期化されていないメモリを残しながら、初期化されていないバッファを提供します。

大規模な割り当ての場合、calloc主流のOS でのほとんどの実装は、OSから既知のゼロ化されたページを(POSIX mmap(MAP_ANONYMOUS)またはWindows などを介してVirtualAlloc)取得するため、ユーザー空間に書き込む必要はありません。これが通常の方法でmallocOSからより多くのページを取得する方法です。callocOSの保証を利用するだけです。

つまり、callocメモリは引き続き「クリーン」で遅延割り当てされ、システム全体で共有されるゼロの物理ページにコピーオンライトがマッピングされます。(仮想メモリを備えたシステムを想定しています。)

一部のコンパイラでは、malloc + memset(0)をcallocに最適化することもできますが、メモリをとして読み取る場合は、callocを明示的に使用する必要があります0

書き込む前にメモリを読み取る予定がない場合はmalloc、OSから新しいページを取得するのではなく、内部の空きリストからダーティメモリを(潜在的に)取得できるように使用します。(または、小さな割り当てのために空きリストのメモリブロックをゼロにする代わりに)。


の埋め込み実装でcalloccalloc、OSがない場合や、プロセス間の情報リークを防ぐためにページをゼロにするファンシーなマルチユーザーOSではない場合、メモリがゼロになる可能性があります。

組み込みLinuxでは、mallocは可能ですmmap(MAP_UNINITIALIZED|MAP_ANONYMOUS)。これは、マルチユーザーシステムでは安全でないため、一部の組み込みカーネルでのみ有効です。


224
* allocバリアントはかなりニーモニックです-clear-alloc、memory-alloc、re-alloc。
Cascabel

43
割り当てられたスペースで使用するすべてのものを設定する場合は、malloc()を使用します。データの一部を初期化しないでおく場合は、calloc()を使用してください。未設定の部分をゼロにすると有益です。
ジョナサンレフラー、

268
callocOSはそれをスピードアップするためにいくつかのトリックを行うことができるので、必ずしもより高価ではありません。FreeBSDがアイドルCPU時間を取得すると、それを使用して、メモリの割り当て解除されたブロックを回避してゼロに設定し、ブロックにマークを付ける単純なプロセスを実行し、プロセスにフラグを付けます。したがって、を実行するとcalloc、最初にそのような事前ゼロ化されたブロックの1つを見つけようとし、それをユーザーに渡します-ほとんどの場合、ブロックを見つけます。
Pavel Minaev、2009年

28
デフォルトでゼロ初期割り当ての結果としてコードが「安全」になると、mallocとcallocのどちらを使用しても、コードの安全性が不十分になると私は思う傾向があります。mallocの使用は、データの初期化が必要であることを示す良い指標です。私は、これらの0バイトが実際に意味がある場合にのみcallocを使用します。また、callocは必ずしもchar型以外の型に対して考えていることを実行するわけではないことに注意してください。トラップ表現を使用したり、IEEE以外の浮動小数点数を使用したりする人は実際にはいませんが、そうでない場合にコードが本当に移植可能であると考えるための言い訳にはなりません。
スティーブジェソップ

18
@SteveJessop "Safer"は正しい言葉ではありません。「決定論的」の方がいいと思います。タイミングとデータシーケンスに依存する障害ではなく、より確定的なコードを使用すると、障害を特定しやすくなります。Callocは、明示的な初期化ではなく、その決定論を簡単に取得できる方法です。
デニス・

362

あまり知られていない違いは、Linuxのような楽観的なメモリ割り当てを備えたオペレーティングシステムでは、によって返されるポインタはmalloc、プログラムが実際に触れるまで、実際のメモリによってバックアップされないことです。

calloc確かにメモリに触れます(メモリにゼロを書き込みます)。したがって、OSが割り当てを実際のRAM(またはスワップ)でバックアップしていることを確認できます。これがmallocよりも遅い理由でもあります(ゼロにする必要があるだけでなく、OSは他のプロセスをスワップアウトして適切なメモリ領域を見つける必要もあります)。

mallocの動作についての詳細は、たとえばこのSOの質問を参照してください


49
callocゼロを書き込む必要はありません。割り当てられたブロックの大部分がオペレーティングシステムによって提供される新しいゼロページで構成されている場合、それらはそのままにされる可能性があります。もちろん、これはcalloc、上の汎用ライブラリ関数ではなく、オペレーティングシステムに合わせて調整する必要がありmallocます。または、実装者はcalloc各単語をゼロと比較する前に、ゼロと比較することができます。これは時間の節約にはなりませんが、新しいページを汚すことはありません。
R .. GitHub ICE HELPING ICEの停止2011年

3
@R ..興味深いメモ。しかし実際には、そのような実装は実際に存在していますか?
Isak Savo、2011年

10
すべてのdlmallocような実装ではmemsetmmap新しい匿名ページ(または同等のページ)を使用してチャンクを取得した場合、はスキップされます。通常、この種の割り当ては、256kから始まる大きなチャンクに使用されます。私は自分のものを除いてゼロを書く前にゼロと比較する実装を知りません。
R .. GitHub ICE HELPING ICEの停止

1
omallocもスキップしmemsetます。callocこれまで、アプリケーション(ページキャッシュ)でまだ使用されていないページに触れる必要はありません。ただし、非常に原始的なcalloc実装は異なります。
mirabilos 2014年

10
glibcのcallocは、OSから新しいメモリを取得しているかどうかを確認します。その場合、mmap(...、MAP_ANONYMOUS)はすでにゼロになっているメモリを返すため、書き込む必要がないことがわかります。
Peter Cordes 2014

112

しばしば見落とされがちな利点の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ます。この場合、大きなオブジェクトは作成できないため、後者は自動的に失敗します。

もちろん、オーバーフローの可能性を単に無視する非準拠の実装に注意する必要があるかもしれません...これが対象のプラットフォームで懸念事項である場合は、とにかく手動でオーバーフローをテストする必要があります。


17
どうやら、算術オーバーフローが2002年にOpenSSHホールを引き起こした原因だったようです。OpenBSDからのメモリ関連機能を伴うこの危険に関する良い記事:undeadly.org/cgi
Philip P.

4
@KomradeP .:興味深い。残念ながら、リンクした記事の冒頭に誤った情報があります。を使用した例charは、オーバーフローではなく、結果をcharオブジェクトに割り当てる際の実装定義の変換です。
R .. GitHub ICE HELPING ICEの停止2014

おそらく、説明のみを目的としています。コンパイラはとにかくそれを最適化する可能性が高いためです。鉱山はこのasmにコンパイルされます。プッシュ1
フィリップP. 14

1
@tristopia:ポイントは、コードがすべての実装で悪用可能であるということではありませんが、追加の前提がなければ正しくないため、正しい/ポータブルな使用法ではありません。
R .. GitHub ICEのヘルプを停止する'20

3
@tristopia:あなたの思考モードが「size_t64ビットなので問題ない」である場合、それはセキュリティバグにつながる欠陥のある考え方です。size_tはサイズを表す抽象型であり、32ビットの数値とaの任意の積size_t(注:sizeof *bar64ビットのC実装では原則として2 ^ 32より大きい可能性があります!)がに収まると考える理由はありませんsize_t
R .. GitHub ICE HELPING ICEを停止する'20

37

ドキュメンテーションは、callocをmallocのように見せます。これはメモリをゼロ初期化するだけです。これは主な違いではありません!callocのアイデアは、メモリ割り当てのコピーオンライトセマンティクスを抽象化することです。callocでメモリを割り当てると、メモリはすべてゼロに初期化された同じ物理ページにマップされます。割り当てられたメモリのいずれかのページが物理ページに書き込まれると、割り当てられます。これは、巨大なハッシュテーブルを作成するためによく使用されます。たとえば、空のハッシュの部分は、追加のメモリ(ページ)によってサポートされないためです。それらは喜んで単一のゼロで初期化されたページを指しており、プロセス間で共有することもできます。

仮想アドレスへの書き込みはページにマップされ、そのページがゼロページである場合、別の物理ページが割り当てられ、ゼロページがそこにコピーされ、制御フローがクライアントプロセスに返されます。これは、メモリマップファイル、仮想メモリなどが機能するのと同じように機能します。ページングを使用します。

このトピックに関する1つの最適化ストーリーを次に示します。http//blogs.fau.de/hager/2007/05/08/benchmarking-fun-with-calloc-and-zero-pages/


26

割り当てられるメモリブロックのサイズに違いはありません。calloc物理的なすべてゼロのビットパターンでメモリブロックを埋めるだけです。実際には、多くの場合に割り当てられたメモリブロックに位置するオブジェクトが想定されcalloc、それらがリテラルで初期化されたかのようinitilial価値を持つ0の値持つ必要があり、すなわち整数0の値-浮動小数点変数、0.0ポインタを-適切なヌル・ポインタ値を、 等々。

ただし、詳細な観点から見ると、calloc(およびmemset(..., 0, ...))は、タイプのオブジェクトを(ゼロで)適切に初期化することのみが保証されていますunsigned char。それ以外はすべて正しく初期化されることが保証されておらず、いわゆるトラップ表現が含まれている可能性があり、これにより未定義の動作が発生します。つまり、unsigned char前述のすべて0のビット以外のタイプのpattermは、不正な値、トラップ表現を表す可能性があります。

その後、Technical Corrigenda to C99標準の1つで、動作はすべての整数型に対して定義されました(これは理にかなっています)。つまり、正式には、現在のC言語では、整数型のみをcalloc(およびmemset(..., 0, ...))で初期化できます。C言語の観点から、それを使用して他の一般的なケースを初期化すると、未定義の動作が発生します。

実際にcallocは、私たち全員が知っているように動作します:)が、それを使用するかどうか(上記を考慮)はあなた次第です。私は個人的にはそれを完全に避け、malloc代わりに使用して自分で初期化することを好みます。

最後に、もう1つの重要な詳細は、要素のサイズに要素の数を掛けてcalloc、最終的なブロックサイズを内部で計算するために必要なことです。その際、calloc算術オーバーフローの可能性を監視する必要があります。要求されたブロックサイズを正しく計算できない場合は、割り当てが失敗します(nullポインター)。その間、mallocバージョンはオーバーフローを監視しようとしません。オーバーフローが発生した場合に備えて、「予測不可能な」量のメモリを割り当てます。


「別の重要な詳細」の段落によると、オーバーフローする可能性があるmemset(p, v, n * sizeof type);ため、問題になるようn * sizeof typeです。for(i=0;i<n;i++) p[i]=v;堅牢なコードにはループを使用する必要があると思います。
chux-2015年

他のnullポインター表現を使用する実装が存在するため、実装がすべてビット0をnullポインターとして使用する必要があることをコードがアサートできる標準的な手段がある場合に役立ちます(それ以外の場合はコンパイルを拒否します)。比較的まれです。そのような実装で実行する必要がないコードは、calloc()またはmemsetを使用してポインターの配列を初期化できれば、より高速になります。
スーパーキャット2016

いいえ@chuxとアレイ場合nエレメントのサイズを有する場合の要素が存在しsizeof type、その後、n*sizeof type任意のオブジェクトの最大サイズ未満でなければならないので、オーバーフローすることはできませんSIZE_MAX
12431234123412341234123 2017

@ 12431234123412341234123 配列サイズ<= について正しいSIZE_MAXが、ここには配列はありません。から返されたポインタは、calloc()を超えた割り当てメモリをポイントできSIZE_MAXます。多くの実装では、2つの引数の積に制限を行うcalloc()にはSIZE_MAX、まだC仕様では、その制限を課しません。
chux-モニカの復活2017年

21

Geoc Hagerのブログのcalloc()とゼロページ使ったベンチマークの楽しみの記事から

calloc()を使用してメモリを割り当てる場合、要求されたメモリの量はすぐには割り当てられません。代わりに、メモリブロックに属するすべてのページは、いくつかのMMUマジック(以下のリンク)によってすべてゼロを含む単一のページに接続されます。そのようなページが読み取られるだけの場合(これは、ベンチマークの元のバージョンの配列b、c、dに当てはまりました)、データは単一のゼロページから提供されます。もちろん、これはキャッシュに適合します。メモリにバインドされたループカーネルについては、これで十分です。ページが(どのように)書き込まれた場合、障害が発生し、「実際の」ページがマップされ、ゼロページがメモリにコピーされます。これはコピーオンライトと呼ばれ、よく知られている最適化アプローチです(C ++の講義で何度も教えました)。その後、


リンクはどこですか?
Rupesh Yadav。

2
回答の1行目には、Georg Hagerのブログへのリンクが含まれています。
Ashish Chavan 2017

11

calloc通常malloc+memsetは0まで

malloc+memset特に次のようなことをしている場合は特に、明示的に使用する方が一般に少し良いです:

ptr=malloc(sizeof(Item));
memset(ptr, 0, sizeof(Item));

sizeof(Item)コンパイル時にコンパイラに認識され、ほとんどの場合コンパイラはメモリをゼロにするための最良の命令に置き換えます。一方、memsetで発生している場合calloc、割り当てのパラメーターサイズはcallocコードにコンパイルされず、実際にmemset呼び出されます。これには通常、長い境界までバイト単位の充填を行うコードが含まれます。sizeof(long)チャンクでメモリを増やし、最後にバイトごとに残りのスペースを埋めます。アロケータがいくつかを呼び出すのに十分スマートである場合でも、aligned_memsetそれはまだ汎用ループです。

1つの注目すべき例外は、非常に大きなメモリチャンク(一部のpower_of_twoキロバイト)のmalloc / callocを実行している場合です。この場合、カーネルから直接割り当てることができます。OSカーネルは通常、セキュリティ上の理由から提供するすべてのメモリをゼロにするので、十分にスマートなcallocは、追加のゼロ化なしでそれを返すだけです。繰り返しますが、小さいことがわかっているものを割り当てるだけの場合は、パフォーマンスの点でmalloc + memsetを使用した方がよい場合があります。


+1は、システムライブラリ内の機能の一般的な実装が、ユーザーコード内の同じ操作よりも必ずしも高速ではないことを示しています。
PatrickSchlüter

1
サイズの乗算calloc()よりも遅くなる2番目のポイントもありmalloc()ます。malloc()はコンパイル時定数を持つことが多いが、calloc()一般的な乗算(size_t64ビットの場合、非常にコストのかかる64ビット* 64ビット= 64ビット演算でも)を使用するために必要です。
PatrickSchlüter

4
glibc callocには、返されたチャンクを最も効率的にクリアする方法を決定するためのいくつかの賢さがあります。メモリはメモリであり、一度に3バイトをクリアすると、保持するためにそれを使用するからといって高速になることはありませんstruct foo { char a,b,c; };。 ed領域全体を常にクリアする場合は、常に+ callocよりも優れています。 size *要素のintオーバーフローも注意深く効率的にチェックします。mallocmemsetmalloccalloc
Peter Cordes 14

8

違い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ブロックのメモリを割り当て、すべてをゼロに初期化する場合、

int *ptr = (int *) calloc(10, (sizeof(int)));

類似点:

両方ともmalloc()calloc()型キャストされていない場合、デフォルトでvoid *を返します。


そして、なぜdata_typeとcast_typeを別々に保つのですか?
完売

7

2つの違いがあります。
まず、引数の数です。malloc()1つの引数(バイト単位で必要なメモリ)を取りますが、calloc()2つの引数が必要です。
第二に、割り当てられたメモリをゼロに初期化するmalloc()一方でcalloc()、割り当てられたメモリを初期化しません。

  • calloc()メモリ領域を割り当てます。長さはそのパラメータの積になります。callocゼロでメモリを埋め、最初のバイトへのポインタを返します。十分なスペースが見つからない場合は、NULLポインターを返します。

構文:ptr_var=(cast_type *)calloc(no_of_blocks , size_of_each_block); ieptr_var=(type *)calloc(n,s);

  • malloc()REQUSTED SIZEの単一メモリブロックを割り当て、最初のバイトへのポインタを返します。必要なメモリ量を見つけられなかった場合は、nullポインタを返します。

構文:ながら機能は、バイト数が割り当てることである一つの引数を取る関数は、要素の数であり、いずれか二つの引数をとり、他のバイトの数は、これらの要素のそれぞれに割り当てること。また、割り当てられたスペースをゼロに初期化しますが、初期化しません。ptr_var=(cast_type *)malloc(Size_in_bytes);malloc()calloc()calloc()malloc()


6

calloc()中で宣言された関数<stdlib.h>ヘッダーが勝る利点をいくつか提供していますmalloc()機能。

  1. 指定されたサイズの要素の数としてメモリを割り当てます。
  2. すべてのビットがゼロになるように割り当てられたメモリを初期化します。

6

malloc()およびcalloc()は、動的メモリ割り当てを可能にするC標準ライブラリの関数です。つまり、どちらも実行時にメモリ割り当てを可能にします。

プロトタイプは次のとおりです。

void *malloc( size_t n);
void *calloc( size_t n, size_t t)

2つの間に主に2つの違いがあります。

  • 動作: malloc()初期化せずにメモリブロックを割り当てます。このブロックからコンテンツを読み取ると、ガベージ値が発生します。calloc()一方、メモリブロックを割り当て、それをゼロに初期化します。明らかに、このブロックのコンテンツを読み取るとゼロになります。

  • 構文:malloc()1つの引数(割り当てられるサイズ)を受け取り、calloc()つの引数(取り、2つの引数(割り当てられるブロックの数と各ブロックのサイズ受け取ります。

両方からの戻り値は、成功した場合、割り当てられたメモリブロックへのポインタです。それ以外の場合は、NULL、メモリ割り当ての失敗を示すが。

例:

int *arr;

// allocate memory for 10 integers with garbage values
arr = (int *)malloc(10 * sizeof(int)); 

// allocate memory for 10 integers and sets all of them to 0
arr = (int *)calloc(10, sizeof(int));

and calloc()を使用して実現できるのと同じ機能:malloc()memset()

// allocate memory for 10 integers with garbage values   
arr= (int *)malloc(10 * sizeof(int));
// set all of them to 0
memset(arr, 0, 10 * sizeof(int)); 

高速であるため、malloc()使用するのが好ましいことに注意してくださいcalloc()。値をゼロで初期化する必要がある場合は、calloc()代わりに使用してください。


5

まだ言及されていない違い:サイズ制限

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_MAX65535が与えられたムーアの法則これは、特定のメモリモデルと2030年について発生される疑いがある、SIZE_MAX == 4294967295ギガバイトの100にし、メモリプール。


2
一般に、size_tは、プログラムが処理できる最大の種類のオブジェクトのサイズを保持できます。size_tが32ビットのシステムでは、4294967295バイトを超える割り当てを処理できない可能性が高く、そのサイズの割り当てを処理できるシステムでは、ほぼ確実にsize_t32ビットよりも大きくなります。唯一の問題は、より小さな割り当てへのポインタを返すのではなくcalloc、積が超過する値を使用してSIZE_MAXゼロを生成することに依存できるかどうかです。
スーパーキャット2016

一般化については同意しますが、C仕様ではcalloc()割り当てを超えることが許可されていSIZE_MAXます。これは過去に16ビットで発生しましたが、size_tメモリが安くなり続けているため、一般的ではないにしても、今後も発生しない理由はありません。
chux-モニカを2016年

1
C標準では、コードがサイズを超える割り当てを要求できるようになっていますSIZE_MAX。確かに、そのような割り当てが成功する可能性がある状況が存在する必要はありません。そのような割り当てを処理できない実装が返す必要があることを義務付けることによる特別な利点があるかどうかはわかりませんNULL(特に、一部の実装でmallocはまだコミットされていないスペースへのリターンポインターがあり、コードが実際に使用しようとすると利用できない場合があるため)それ)。
スーパーキャット2016

さらに、利用可能なアドレッシング範囲が表現可能な最大の整数を超えるシステムが過去にあった可能性がある場合、数十億ギガバイトのストレージ容量が必要になるため、それが再び発生する現実的な可能性はありません。ムーアの法則が続いたとしても、32ビットで十分でなくなった時点から、64ビットで十分でなくなった時点まで、16ビットで十分だった時点から32ビットではなかった時点までの2倍の時間がかかります。ない
スーパーキャット2016

1
4Gを超える単一の割り当てに対応できる実装が定義さsize_tれないのはuint64_tなぜですか?
スーパーキャット2016

2

ブロック数:
malloc()は、要求されたメモリの単一ブロックを割り当てます。
calloc()は要求されたメモリの複数のブロックを割り当てます

初期化:
malloc()-割り当てられたメモリをクリアして初期化しません。
calloc()-割り当てられたメモリをゼロで初期化します。

速度:
malloc()は高速です。
calloc()はmalloc()より低速です。

引数と構文:
malloc()は1つの引数を取ります:

  1. バイト

    • 割り当てられるバイト数

calloc()は2つの引数を取ります。

  1. 長さ

    • 割り当てられるメモリのブロック数
  2. バイト
    • メモリの各ブロックに割り当てられるバイト数
void *malloc(size_t bytes);         
void *calloc(size_t length, size_t bytes);      

メモリ割り当ての方法:
malloc関数は、使用可能なヒープから目的の「サイズ」のメモリを割り当てます。
calloc関数は、「num * size」に等しいサイズのメモリを割り当てます。

名前の意味:
名前mallocは「メモリ割り当て」を意味します。
名前callocは「連続割り当て」を意味します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.