見出し画像

続C言語教室 - 第15回 動的メモリの使い方(その3)

さて、malloc や calloc でヒープから実行時に任意のサイズのメモリを割り当てる方法を調べてきましたが、今回は一度確保したメモリの大きさを変える方法です。

続C言語教室 - 第14回 動的メモリの使い方(その2)

好きな大きさのメモリを確保したものの、何らかの理由で、その大きさを大きくしたり、小さくしたりしたくなることも無いとは言えません。ファイルを読んでみて、最後にそれで終わりかと思っていたら、読んでいる間にファイルが大きくなっていたなんて言うこともあるにはありますしね。

既に確保したメモリの中身が不要であるのなら、素直に一旦解放して、新たに必要なサイズのメモリを確保すれば良いのですが、中身を保ったまま大きくしたくなることのほうが普通です。そんな時には realloc というライブラリ関数を使います。

void *realloc(
  void *memblock,
  size_t size
);

reallocのプロトタイプ

これは既に確保したメモリへのポインタと、割り当てて欲しい領域(追加分ではなく全体)のバイト単位での値を渡して、指定した大きさのメモリ領域へのポインタを返してくれます。新しい大きさでメモリが確保されなかった場合には、mallocと同様にNULLが戻ります。この時、渡した以前の大きさの領域はそのままで中身も変化はありません。

ここでreallocが返したポインタのアドレスは、渡したアドレスと同じ時もありますし、新しくなる場合もあります。新しくなった場合には、以前の領域の中身がコピーされていますが、追加された領域の中身に関しては初期化されず未定義です。残念なことに calloc のように初期化してくれるなんていう技はありません。もし初期化したければ少しばかり込み入った式を作って追加分だけ初期化のループを回すか、memset でクリアすることになります。

【C言語】realloc関数|正しい使い方と注意点 メモリ断片化など

ところで realloc が NULL を返した場合、先にも説明したように元の領域は「そのまま」です。続けてその大きさで使い続けるのであれば良いのですが、もしエラーだからといって処理を中断してしまうのであれば、元の領域をあらためて解放しなければ、ここでメモリリークが発生します。これ忘れやすいところです。

警告 C6308

もちろん、realloc に「渡す」ポインタは malloc や calloc で確保されたポインタであることが必要で、それ以外のアドレス、free で解放してしまったポインタや、もちろん alloca で押さえたポインタを使えば(運が良ければ)実行時エラーとなります(運が悪ければヒープが破壊される)。そして最終的に free を呼び出して解放しなければなりません。

realloc でサイズを「縮小」した場合、そのサイズまでの領域はそのままですが、縮小された部分の中身は呼び出した直後はそのままの可能性は高いですが、もう読み書きしてはいけません。たまたま残っているだけで、新たなヒープの割り当てて他の場所で使われる領域になっています。稀にハマるのがメモリアライメントの関係で、有効な領域のすぐ後ろ、無効な領域の最初のメモリはずっと再利用されることがない場合もあり、間違いに気が付かないことがあることです。ループ条件の末尾が1周分判断が誤っていた場合、何も起こらずにスルーしてしまうことがあります。文字列終端の分を考慮していない駄目なコードなのに気が付かなかったことがありました。

C言語にて一度、mallocで確保したメモリサイズを縮小した際、縮小されない?

そんなことを気にしだせば、いったい malloc (やcalloc)は、どのように動的なメモリを管理しているかが気になってきますよね。初期のUNIXでは単純なフリーエリアのリンクリストで管理していたのですが、これにはいろいろな問題があり、その後はもう少し込み入った処理をするようになりました。またメモリ割り当てでエラーとなった場合の処理って、実際にメモリが不足することはそんなに無いので、エラー処理のデバッグが十分ではないことがあります。それにヒープ周りのポインタで何らかのバグを踏んだ場合って、その原因を探すのに苦労することが多いです。次回はその辺りを調べてみます。

ヘッダ画像は、AIで生成しました。

#プログラミング #C言語 #プログラミング講座 #C言語教室 #動的メモリ #ヒープ #拡大 #縮小 #サイズ変更 #初期化 #エラー処理 #ループ条件

いいなと思ったら応援しよう!

kzn
頂いたチップは記事を書くための資料を揃えるために使わせていただきます!