Cでmallocを使用する必要がある場合と使用しない場合


93

malloc()の仕組みを理解しています。私の質問は、私はこのようなものを見るでしょう:

#define A_MEGABYTE (1024 * 1024)

char *some_memory;
size_t size_to_allocate = A_MEGABYTE;
some_memory = (char *)malloc(size_to_allocate);
sprintf(some_memory, "Hello World");
printf("%s\n", some_memory);
free(some_memory);

簡潔にするために、エラーチェックを省略しました。私の質問は、メモリ内のいくつかの静的ストレージへのポインタを初期化することで上記のことを実行できないのですか?おそらく:

char *some_memory = "Hello World";

どの時点で、保持する必要のある値を宣言/初期化する代わりに、実際にメモリを割り当てる必要がありますか?


5
再:私は簡潔にするためにエラーチェックを省略 -残念ながら、あまりにも多くのプログラマがエラーチェックを省略し、彼らは気付いていないため、malloc()失敗する可能性があります!
アンドリュー

回答:


131
char *some_memory = "Hello World";

文字列定数へのポインタを作成しています。つまり、「Hello World」という文字列はメモリの読み取り専用部分のどこかにあり、ポインタを持っているだけです。文字列は読み取り専用として使用できます。変更することはできません。例:

some_memory[0] = 'h';

トラブルを求めています。

一方

some_memory = (char *)malloc(size_to_allocate);

char配列(変数)を割り当てており、some_memoryは割り当てられたメモリを指します。これで、この配列は読み取りと書き込みの両方が可能です。これで次のことができます。

some_memory[0] = 'h';

配列の内容が「hello World」に変わります


19
明確にするために、私がこの答えを好きなだけ(私はあなたに+1を与えました)、文字配列を使用するだけでmalloc()なしで同じことができます。次のようなもの:char some_memory [] = "Hello"; some_memory [0] = 'W'; も動作します。
randombits 2009

18
あなたは正しいです。出来るよ。malloc()を使用すると、メモリは実行時に動的に割り当てられるため、コンパイル時に配列サイズを修正する必要はありません。また、realloc()を使用して配列サイズを拡大または縮小することもできます。 [] = "こんにちは"; ここでは配列の内容を変更できますが、サイズは固定されています。したがって、ニーズに応じて、3つのオプションのいずれかを使用します。1)char constへのポインター2)動的に割り当てられた配列3)固定サイズ、コンパイル時に割り当てられた配列。
codaddict 2009

読み取り専用であることを強調するには、次のように記述するconst char *s = "hi";必要があります。これは実際には標準で必須ではありませんか?
タイスティル

@Till、いいえ。文字列リテラル「hi」のベースアドレスに初期化されたポインタを宣言したからです。sは、非const charを指すように完全に合法的に再割り当てできます。読み取り専用文字列への定数ポインタが必要な場合は、次のものが必要ですconst char const* s;
Rob11311

38

その正確な例では、mallocはほとんど役に立ちません。

mallocが必要になる主な理由は、コードスコープとは異なる有効期間を持つ必要があるデータがある場合です。コードは1つのルーチンでmallocを呼び出し、ポインターをどこかに格納し、最終的には別のルーチンでfreeを呼び出します。

2番目の理由は、Cには、割り当てのためにスタックに十分なスペースが残っているかどうかを知る方法がないためです。コードを100%堅牢にする必要がある場合は、mallocを使用する方が安全です。これは、コードが割り当ての失敗を認識して処理できるためです。


4
メモリのライフサイクル、およびいつ、どのように割り当てを解除するかという関連する質問は、多くの一般的なライブラリとソフトウェアコンポーネントの重要な問題です。それらには通常、十分に文書化されたルールがあります。「このルーチンの1つへのポインターを渡す場合は、mallocを実行する必要があります。追跡して、終了したら解放します。 」厄介なバグの一般的な原因は、静的に割り当てられたメモリへのポインタをそのようなライブラリに渡すことです。ライブラリがfree()しようとすると、プログラムがクラッシュします。私は最近、他の誰かが書いたようなバグの修正に多くの時間を費やしました。
ボブマーフィー

malloc()が実際に使用されるのは、プログラムの寿命の間に複数回呼び出され、複数回呼び出され、「クリーンアップ」する必要があるコードのセグメントがある場合だけです。 ()はfree()を伴いますか?たとえば、ホイールオブフォーチュンのようなゲームでは、入力を指定されたchar配列に推測して入力した後、malloc()サイズの配列を次の推測のために解放できますか?
スミスは

データの存続期間は、実際にmallocを使用する本当の理由です。抽象データ型がモジュールで表され、リスト型、およびリストに項目を追加/削除するルーチンを宣言するとします。これらのアイテム値は、動的に割り当てられたメモリにコピーする必要があります。
Rob11311 2014年

@ボブ:これらの厄介なバグは、アロケータがメモリを解放するという規則をはるかに優れたものにします。ブロック全体に対して1回だけfreeを呼び出す必要があるため、参照の局所性を向上させるためにcallocを使用してメモリを割り当て、それらのライブラリの壊れた性質を公開するとします。幸い、メモリを「malloc-ed」するように指定するライブラリを使用する必要はありませんでした。これはPOSIXの伝統ではなく、バグと見なされる可能性が非常に高いです。彼らがあなたがmallocを使わなければならないことを「知っている」なら、なぜライブラリルーチンはあなたのためにそれをしないのですか?
Rob11311 2014年

17

mallocは、コンパイル時に処理されるためサイズを変更できないhello worldの例のような静的宣言と比較して、実行時にメモリを割り当て、再割り当て、解放するための素晴らしいツールです。

したがって、Mallocは、ファイルの内容の読み取りやソケットの処理など、任意のサイズのデータ​​を処理し、処理するデータの長さがわからない場合に常に役立ちます。

もちろん、あなたが与えたような簡単な例では、mallocは魔法の「適切なジョブに適したツール」ではありませんが、より複雑なケース(たとえば、実行時に任意のサイズの配列を作成する)の場合は、これが唯一の方法です行く。


7

使用する必要があるメモリの正確なサイズがわからない場合は、動的割り当て(malloc)が必要です。たとえば、ユーザーがアプリケーションでファイルを開いたときなどです。ファイルの内容をメモリに読み込む必要がありますが、ユーザーが実行時にその場でファイルを選択するため、ファイルのサイズは事前にわかりません。したがって、基本的にmallocは、作業するデータのサイズが事前にわからない場合に必要です。少なくともそれがを使用する主な理由の1つですmalloc。コンパイル時にサイズがわかっている(さらに、変更したくない)単純な文字列を使用した例では、動的に割り当てることはあまり意味がありません。


少しトピックから外れていますが、...を使用するときにメモリリークが発生しないように注意する必要がありますmalloc。このコードを考えてみましょう:

int do_something() {
    uint8_t* someMemory = (uint8_t*)malloc(1024);

    // Do some stuff

    if ( /* some error occured */ ) return -1;

    // Do some other stuff

    free(someMemory);
    return result;
}

このコードの何が問題になっていますか?mallocとの間に条件付きreturnステートメントがありfreeます。最初は大丈夫に思えるかもしれませんが、考えてみてください。エラーが発生した場合は、割り当てたメモリを解放せずに戻ります。これは、メモリリークの一般的な原因です。

もちろん、これは非常に単純な例であり、ここで間違いを見つけるのは非常に簡単ですが、ポインタ、mallocs、frees、およびあらゆる種類のエラー処理が散らかった数百行のコードを想像してください。物事は本当に乱雑になる可能性があります。これは、適用可能な場合にCよりも現代のC ++をはるかに好む理由の1つですが、それはまったく別の話題です。

そのためmalloc、を使用するときは常に、メモリがfreeできるだけ少ないことを確認してください。


素晴らしい例!^ _ ^への道
Musa Al-hassy

6
char *some_memory = "Hello World";
sprintf(some_memory, "Goodbye...");

は不正constです。文字列リテラルはです。

これにより、12バイトのchar配列がスタック上またはグローバルに割り当てられます(宣言されている場所によって異なります)。

char some_memory[] = "Hello World";

さらに操作する余地を残したい場合は、配列のサイズを大きくするように指定できます。(ただし、スタックに1MBを入れないでください。)

#define LINE_LEN 80

char some_memory[LINE_LEN] = "Hello World";
strcpy(some_memory, "Goodbye, sad world...");
printf("%s\n", some_memory);

5

メモリを割り当てる必要がある1つの理由は、実行時にメモリを変更する場合です。その場合、mallocまたはスタック上のバ​​ッファーを使用できます。「Hello World」をポインターに割り当てる簡単な例では、実行時に「通常」は変更できないメモリを定義します。

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