C++ での new/delete と malloc/free の詳細な説明


序文

C++ の学習に興味がある場合は、この記事を読むことができます: C/C++ チュートリアル

一、new/delete

1. 前文

C++ プログラミングでは、動的メモリ割り当ては非常に一般的な操作です。new と delete は C++ で提供される動的メモリ割り当て演算子であり、任意のタイプのメモリを動的に割り当てるために使用でき、メモリ ブロックのサイズを明示的に指定する必要はありません。

2.使い方

2.1. new と delete の基本構文

new と delete は、メモリを動的に割り当てて解放するための C++ のキーワードです。new と delete の基本的な構文は次のとおりです。

// 动态分配一个对象
Type* p = new Type;

// 释放已经分配的对象
delete p;

このうち、動的に割り当てるデータ型Typeを表し、pデータ型へのポインタです。new Type操作が実行されると、システムはこのタイプのオブジェクトにメモリ ブロックを割り当て、このメモリ ブロックへのポインタを返します。オブジェクトが不要になったら、delete操作。

同時に複数のオブジェクトを割り当てる必要がある場合は、次の構文を使用できます。

// 动态分配一个数组
Type* p = new Type[n];

// 释放已经分配的数组
delete [] p;

このうち、 は割り当てるオブジェクトの数nを表し、pの配列へのポインタです。Type

動的メモリ割り当てに使用する場合、メモリが不足すると例外がスローされることnew注意してください。std::bad_allocしたがって、この例外はプログラムで処理する必要があります。

2.2. new と delete の基本的な実装原則

new と delete にも、基礎となる実装で注意すべき詳細がいくつかあります。new Type操作を実行すると、実際には次の手順が順番に実行されます。

  1. 演算子 new 関数を呼び出して、サイズsizeof(Type)が。
  2. クラスのコンストラクターを呼び出して、要求されたメモリ空間を初期化します。
  3. 要求されたメモリ空間へのポインターを返します。

delete p操作を実行すると、実際には次の手順が順番に実行されます。

  1. クラスのデストラクタを呼び出して、オブジェクトが占有しているリソースを解放します。
  2. 演算子 delete 関数を呼び出して、オブジェクトが占有しているメモリを解放します。

その中で、operator new と operator delete は C++ で提供されるキーワードです。演算子 new 関数はメモリの適用を担当し、演算子 delete 関数はメモリの解放を担当します。

なお、malloc/free とは異なり、new/delete はクラスのコンストラクタとデストラクタを呼び出し、必要なメモリ空間サイズを自動的に計算できます。これは、new/delete を使用する大きな利点でもあります。

3. 基本原則

3.1. operator new 和 operator delete

C++ の operator new 関数と operator delete 関数は、動的にメモリを割り当てて解放するために使用されます。演算子 new 関数はメモリの適用を担当し、演算子 delete 関数はメモリの解放を担当します。

演算子 new の実装の 1 つを次に示します。

void* operator new(size_t size) {
    
    
    if (size == 0) {
    
    
        size = 1;
    }
    void* ptr = malloc(size);
    if (!ptr) {
    
    
        throw std::bad_alloc();
    }
    return ptr;
}

このうち、適用するメモリ空間のサイズsizeを示します。値が 0 の場合は、1 に設定します。次に、malloc 関数を呼び出して、指定されたサイズのメモリ空間を適用します。アプリケーションが失敗すると、std::bad_alloc例外が。

以下は、演算子 delete の実装です。

void operator delete(void ptr) noexcept {
    
    
    free(ptr); 
}

解放するメモリ空間ポインタはどこにptrあります。ここでnoexceptキーワード関数が例外をスローしないことを示します。

operator new および operator delete 関数を使用する場合は、クラスのコンストラクターとデストラクターを自分で呼び出す必要があることに注意してください。

3.2. new と delete の基本的な実装原則

new および delete キーワードは、プログラマーの利便性のために、基本的な実装で operator new および operator delete 関数を実際にカプセル化し、オーバーロードします。new と delete の実装の 1 つを次に示します。

void* operator new(size_t size) {
    
    
    if (size == 0) {
    
    
        size = 1;
    }
    void* ptr = malloc(size);
    if (!ptr) {
    
    
        throw std::bad_alloc();
    }
    return ptr;
}

void operator delete(void* ptr) noexcept {
    
    
    free(ptr);
}

void* operator new[](size_t size) {
    
    
    if (size == 0) {
    
    
        size = 1;
    }
    void* ptr = malloc(size);
    if (!ptr) {
    
    
        throw std::bad_alloc();
    }
    return ptr;
}

void operator delete[](void* ptr) noexcept {
    
    
    free(ptr);
}

ご覧のとおり、new と delete は、実際には new 演算子と delete 演算子をオーバーロードしています。

複雑なデータ構造の場合、new[] を使用して複数のメモリに適用する場合、現在のアプリケーションの数を格納するために追加の 4 バイト メモリが適用されることに注意してください。オブジェクトが呼び出されますが、このデータは外部からは見えません

4. 注意事項

動的メモリ割り当てに new/delete を使用する場合は、次の点に注意する必要があります。

  • メモリ リーク: 使用されなくなったメモリは、時間内に解放する必要があります。そうしないと、メモリ リークが発生する可能性があります。
  • ダングリング ポインター: 解放されたメモリ ブロック ポインターにはアクセスできなくなります。そうしないと、プログラムがクラッシュしたり、その他の予期しないエラーが発生したりする可能性があります。
  • メモリを繰り返し解放しないでください。同じメモリ ブロックを解放できるのは 1 回だけです。そうしないと、プログラムのクラッシュやその他の予期しないエラーが発生する可能性があります。
  • マルチスレッド環境: 複数のスレッドが同じメモリ ブロックに同時にアクセスする場合、適切な同期メカニズムを採用して、スレッドの安全性を確保する必要があります。

5. まとめ

C++ プログラミングでは、特定の状況に応じて適切な動的メモリ割り当て方法を選択する必要があります.動的メモリ割り当てを使用する場合は、プログラムの正確性と安定性を確保するために、適切なプログラミング習慣に従う必要があります. 同時に、プログラムの堅牢性と安定性を確保するために、メモリ リーク、ダングリング ポインタ、メモリの解放の繰り返しなどの問題も回避する必要があります。

2、malloc/free

1. 前文

malloc と free は C で提供される動的メモリ割り当て関数であり、任意のタイプのメモリを動的に割り当てるために使用でき、メモリ ブロックのサイズを明示的に指定する必要はありません。

2.使い方

2.1. malloc と free の基本構文

malloc と free は、メモリを動的に割り当てて解放するために使用される C の関数です。以下は、malloc と free の基本的な構文です。

// 动态分配一块内存
Type* p = (Type*)malloc(sizeof(Type));

// 释放已经分配的内存
free(p);

このうち、動的に割り当てるデータ型Typeを表し、pデータ型へのポインタです。malloc(sizeof(Type))操作が実行されると、システムはこのタイプのオブジェクトにメモリ ブロックを割り当て、このメモリ ブロックへのポインタを返します。オブジェクトが不要になったら、free操作。

malloc動的メモリ割り当てに使用する場合、メモリ不足が発生した場合は null ポインタが返されることに注意してください。したがって、この状況はプログラムで処理する必要があります。

2.2. malloc と free の基本的な実装原則

Malloc と free にも、基礎となる実装で注意すべき詳細がいくつかあります。malloc(sizeof(Type))操作を実行すると、実際には次の手順が順番に実行されます。

  1. システム関数 sbrk を呼び出して、プログラムのデータ セグメントを展開します。(Windows は、この関数を実現するために win API を呼び出します)
  2. 要求されたメモリ ブロックを使用中のメモリ ブロックとリンクします。
  3. 要求されたメモリ空間へのポインターを返します。

free(p)操作を実行すると、実際には次の手順が順番に実行されます。

  1. p が指すメモリのブロックを未使用としてマークします。
  2. p が指すメモリ ブロックを他の未使用のメモリ ブロックとマージします。
  3. マージされたメモリ ブロックが占有されていない場合、メモリ ブロックは解放されます。

動的メモリ割り当てに malloc/free を使用する場合、クラスのコンストラクタとデストラクタを自分で呼び出す必要があり、必要なメモリ容量を計算できないことに注意してください。

3. 基本原則

3.1. sbrk 関数

sbrk 関数は、プログラムのデータ セグメントを展開するために使用されるシステム コールです。Linux システムでは、sbrk 関数は現在のヒープ トップ アドレスを返すことができ、指定されたバイト数だけヒープ トップ アドレスを上下に移動できます。

以下は、sbrk 関数を使用して現在のヒープ トップ アドレスを取得する方法を示す簡単な例です。

#include <unistd.h>
#include <iostream>

int main() {
    
    
    void* p1 = sbrk(0);  // 获取当前堆顶部地址
    std::cout << "p1 = " << p1 << std::endl;

    void* p2 = sbrk(1024);  // 将堆顶部地址向上移动 1024 字节
    std::cout << "p2 = " << p2 << std::endl;

    void* p3 = sbrk(-512);  // 将堆顶部地址向下移动 512 字节
    std::cout << "p3 = " << p3 << std::endl;

    void* p4 = sbrk(0);  // 再次获取当前堆顶部地址
    std::cout << "p4 = " << p4 << std::endl;

    return 0;
}

ウィンドウ システムには、ヒープ メモリの割り当てに使用できる独自の win API もあります。

3.2. メモリブロックの管理

malloc と free も、基盤となる実装でメモリ ブロックを管理する必要があります。動的メモリ割り当てに malloc を使用する場合、システムは要求されたメモリ ブロックにいくつかの追加情報を追加します。

たとえば、メモリ ブロックのサイズ、次のメモリ ブロックへのポインタなどです。これらの情報はメモリ ブロックの先頭に保存され、ユーザ プログラムのメモリ空間へのアクセスには影響しません。

以下は、malloc 関数を使用してメモリ ブロックを取得する方法と、メモリ ブロックに含まれる情報を示す簡単な例です。

#include <iostream>
#include <cstdlib>

int main() {
    
    
    int* p = (int*)malloc(sizeof(int) * 10);  // 动态分配一块内存,可以存放 10 个 int 类型的变量
    if (!p) {
    
    
        std::cout << "Memory allocation failed" << std::endl;
        return -1;
    }

    std::cout << "Allocate memory at address " << p << std::endl;
    std::cout << "The size of the memory block is " << sizeof(int) * 10 << " bytes" << std::endl;

    int* next_p = (int*)(((char*)p) + sizeof(int) * 10);
    std::cout << "The pointer to the next memory block is " << next_p << std::endl;

    free(p);  // 释放内存

    return 0;
}

3.3. メモリーのアライメント

動的メモリ割り当てに malloc を使用する場合、メモリ アラインメントの問題を考慮する必要があります。

いわゆるメモリー・アラインメントとは、メモリー内でデータ型が配置されるアドレスが特定の条件を満たす必要があることを意味します。

具体的には、各データ型にはアラインメントが関連付けられており、通常はデータ型のサイズと同じです (たとえば、int のアラインメントは 4 バイトです)。メモリを割り当てるとき、システムは、要求されたメモリ ブロックの開始アドレスがアライメント値の整数倍であることを確認します。

以下は、malloc 関数を使用してメモリ ブロックを取得する方法と、メモリ アラインメントの効果を示す簡単な例です。

#include <iostream>
#include <cstdlib>

struct MyStruct {
    
    
    double x;
    char c;
    int i;
};

int main() {
    
    
    std::cout << "Size of MyStruct is " << sizeof(MyStruct) << " bytes" << std::endl;

    MyStruct* p1 = (MyStruct*)malloc(sizeof(MyStruct));
    if (!p1) {
    
    
        std::cout << "Memory allocation failed" << std::endl;
        return -1;
    }

    std::cout << "Allocate memory at address " << p1 << std::endl;

    char* p2 = (char*)p1;
    for (int i = 0; i < sizeof(MyStruct); ++i) {
    
    
        std::cout << (int)p2[i] << " ";
    }
    std::cout << std::endl;

    free(p1);  // 释放内存

    return 0;
}

MyStruct 型のメモリを割り当てる場合、システムは返される開始アドレスが double 型のサイズの整数倍であることを確認します。

4. 注意事項

動的メモリ割り当てに malloc/free を使用する場合、次の点に注意する必要があります。

  • メモリ リーク: 使用されなくなったメモリは、時間内に解放する必要があります。そうしないと、メモリ リークが発生する可能性があります。
  • ダングリング ポインター: 解放されたメモリ ブロック ポインターにはアクセスできなくなります。そうしないと、プログラムがクラッシュしたり、その他の予期しないエラーが発生したりする可能性があります。
  • メモリを繰り返し解放しないでください。同じメモリ ブロックを解放できるのは 1 回だけです。そうしないと、プログラムがクラッシュしたり、

その他の予期しないエラー。

  • 範囲外のメモリ: 解放されたメモリ ブロックにアクセスしたり、割り当てられたメモリ ブロックの範囲を超えるアドレスにアクセスしたりすると、プログラムがクラッシュしたり、その他の予期しないエラーが発生したりする可能性があります。
  • malloc/free と new/delete を混在させないでください: 1 つのメモリ割り当て方法を同じプログラムで均一に使用する必要があります。malloc/free と new/delete を混在させないでください。メモリ管理の問題が発生する可能性があります。

5. new/delete と malloc/free の違い

new と delete は C++ で提供される動的メモリ割り当て演算子であり、malloc/free と機能的に似ています。

new/delete の使用は、malloc/free よりも単純で直感的です。さらに、新規/削除には次の利点があります。

  • 型の安全性: new/delete は、手動で指定することなく、型に応じて必要なメモリ空間サイズを自動的に計算できます。
  • コンストラクタとデストラクタを自動的に呼び出す: new はクラスのコンストラクタを自動的に呼び出し、delete はクラスのデストラクタを自動的に呼び出すことができるため、オブジェクトのライフサイクルの管理が容易になります。
  • オーバーロードのサポート: new/delete 演算子をオーバーロードできるため、いくつかの特別な機能を実現できます。

動的メモリ割り当てに new/delete を使用する場合、メモリ リーク、ダングリング ポインタ、メモリ範囲外などの問題も発生する可能性があることに注意してください。したがって、プログラムを作成するときは、上記の問題を回避するために、動的メモリの割り当てと解放の問題を慎重に処理する必要があります。

6. まとめ

動的メモリ割り当てを使用するプロセスでは、メモリ リーク、ダングリング ポインター、メモリの範囲外などの問題に注意を払う必要があり、特定の状況に応じて適切なメモリ割り当て方法を選択する必要もあります。

おすすめ

転載: blog.csdn.net/weixin_50964512/article/details/130075826