【C/C++】new/deleteとmalloc/freeの違いを徹底解説!メモリ管理の基本
C/C++には、実行時にメモリを動的に確保・解放するための仕組みとして、C++のnew/delete演算子と、C言語由来のmalloc()/free()関数があります。どちらもヒープ領域を扱う点は共通していますが、性質や使い方には重要な違いがあります。本記事では、それぞれの基本的な使い方をサンプルコードとともに紹介し、最後に両者の主な相違点を比較表で整理します。
new / delete(C++)
new演算子は、ヒープ領域へのメモリ割り当てを要求します。十分な空きメモリが存在すれば、確保したメモリを初期化してポインタ変数に結び付け、そのアドレスを返します。
delete演算子は、newで確保したメモリを解放するために使用します。プログラマは、不要になったポインタ変数が指すメモリを、任意のタイミングで解放できます。
以下は、C++におけるnew/delete演算子の使用例です。
サンプルコード
#include <iostream>
using namespace std;
int main () {
int *ptr1 = NULL;
ptr1 = new int;
float *ptr2 = new float(299.121);
int *ptr3 = new int[28];
*ptr1 = 28;
cout << "Value of pointer variable 1 : " << *ptr1 << endl;
cout << "Value of pointer variable 2 : " << *ptr2 << endl;
if (!ptr3)
cout << "Allocation of memory failed\n";
else {
for (int i = 10; i < 15; i++)
ptr3[i] = i+1;
cout << "Value of store in block of memory: ";
for (int i = 10; i < 15; i++)
cout << ptr3[i] << " ";
}
delete ptr1;
delete ptr2;
delete[] ptr3;
return 0;
}
実行結果
Value of pointer variable 1 : 28 Value of pointer variable 2 : 299.121 Value of store in block of memory: 11 12 13 14 15
この例では、単一のint型・float型のメモリ確保に加え、配列として28個分のint型メモリを確保しています。配列として確保したメモリを解放する際は、delete[]を使用する点に注意してください。
malloc / free(C言語)
malloc()関数は、指定されたサイズ(バイト数)のメモリを確保し、その先頭バイトへのポインタを返します。確保に失敗した場合はNULLポインタを返します。
free()関数は、malloc()で確保したメモリを解放するために使用します。free()を呼び出してもポインタ変数の値そのものは変化しないため、ポインタは引き続き同じメモリ位置を指し続けます。解放済みのポインタにアクセスしないよう注意しましょう。
以下は、C言語におけるmalloc/freeの使用例です。
サンプルコード
#include <stdio.h>
#include <stdlib.h>
int main() {
int n = 4, i, *p, s = 0;
p = (int*) malloc(n * sizeof(int));
if(p == NULL) {
printf("\nError! memory not allocated.");
exit(0);
}
printf("\nEnter elements of array : ");
for(i = 0; i < n; ++i) {
scanf("%d", p + i);
s += *(p + i);
}
printf("\nSum : %d", s);
free(p);
return 0;
}
実行結果
Enter elements of array : 32 23 21 8 Sum : 84
この例では、4個分のint型メモリをmalloc()で確保し、ユーザーの入力値を格納して合計を表示しています。確保が成功したかどうかをNULLチェックで確認してから使用している点もポイントです。
new/delete と malloc/free の主な違い
| 項目 | new / delete | malloc / free |
|---|---|---|
| 種類 | 演算子 | ライブラリ関数 |
| 対応言語 | C++専用 | C / C++の両方で使用可能 |
| サイズの指定 | 型から自動的に計算される | sizeofなどを用いてバイト数を明示する必要がある |
| 戻り値の型 | 要求した型のポインタ | void*(キャストが必要な場合がある) |
| コンストラクタ/デストラクタ | new時にコンストラクタ、delete時にデストラクタが呼ばれる | 呼ばれない(純粋なメモリの確保・解放のみ) |
| 失敗時の挙動 | std::bad_alloc例外をスローする | NULLを返す |
| 再確保 | 直接はサポートされない | realloc()で拡張・縮小が可能 |
まとめ
C++でオブジェクト指向的なプログラムを書く場合、コンストラクタやデストラクタが適切に呼び出されるnew/deleteを使うのが基本です。一方、C言語との互換性が必要な場面や、realloc()による柔軟な再確保を行いたい場合にはmalloc()/free()が有効です。ただし、同一プログラム内で両者を混在させると、解放忘れや二重解放などの不具合につながりやすいため、原則としてどちらか一方に統一することをおすすめします。
-
C++におけるドット演算子(.)とアロー演算子(->)の違いを徹底解説
C++では、ドット演算子(.)とアロー演算子(->)はどちらもクラスのメンバーにアクセスするために使用されますが、それぞれ使用される場面が異なります。C++において、class、struct、unionとして宣言された型はすべて「クラスタイプ」とみなされるため、以下の説明はこれら3つすべてに当てはまります。基本的な使い分けa.b は、b がオブジェクト a(またはオブジェクトへの参照[1])のメンバーである場合にのみ使用できます。つまり、a.b の形式では、a は常にクラスの実際のオブジェクト(またはその参照)でなければなりません。a->b は、本質的に (*a).b の省略記法で
-
C++における#defineとconstキーワードの違いとは?
#defineとconstの基本的な違い#defineディレクティブはプリプロセッサディレクティブです。プリプロセッサは、コンパイラがソースコードを認識する前に、マクロをその定義内容で単純に置き換えます。イメージとしては、ソースコード全体に対して自動的に実行される「検索と置換」のようなものです。const変数の特徴一方、constによる変数宣言は、言語仕様上の実際の変数を宣言するものです。そのため、通常の変数と同じように扱うことができます。具体的には、以下のような操作が可能です。アドレスの取得(ポインタや参照への代入)関数への引数としての受け渡しキャストや型変換パフォーマンスとスコープの違いコ