C++/C的内存分布

图:

说明:

  1. 栈又叫堆栈--非静态局部变量/函数参数/返回值等等,栈是向下增长的。
  2. . 内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享内存,做进程间通信。
  3. 堆用于程序运行时动态内存分配,堆是可以上增长的。
  4.  数据段--存储全局数据和静态数据。
  5.  代码段--可执行的代码/只读常量
     

C语言中的内存管理

malloc

  1. 功能:用于在堆区申请一块连续的指定大小的内存块区域,并以void*类型返回分配的内存区域地址。
  2. 初始化:不会进行内存初始化,即分配的内存区域可能包含任意数据。
  3. 参数:一个参数,即需要分配的内存大小(以字节为单位)。
  4. 返回值:返回一个指向分配内存的指针,如果分配失败则返回NULL。
  5. 使用场景:适用于在程序运行时根据需要动态分配内存的情况,特别是当内存不需要初始化时。
  6. 示例:
    #include <stdio.h>
    #include <stdlib.h>
    
    int main() {
        int *ptr;
        int n = 5;
        // 使用 malloc 分配内存
        ptr = (int*)malloc(n * sizeof(int));
        if (ptr == NULL) {
            printf("Memory not allocated.\n");
            exit(1);
        }
        // 初始化内存
        for (int i = 0; i < n; i++) {
            ptr[i] = i + 1;
        }
        // 打印数组内容
        for (int i = 0; i < n; i++) {
            printf("%d ", ptr[i]);
        }
        // 释放内存
        free(ptr);
        return 0;
    }

  

calloc

  1. 功能:与malloc类似,也是用于动态地分配内存空间。
  2. 初始化:会在分配内存时自动将内存清零,即将分配的内存区域的每一位都初始化为零。因此,calloc也被称为“零初始化内存分配器”。
  3. 参数:两个参数,第一个参数表示要分配的元素个数,第二个参数表示每个元素的大小(以字节为单位)。
  4. 返回值:返回一个指向分配内存的指针,如果分配失败则返回NULL。
  5. 使用场景:适用于需要分配并初始化内存的情况,特别是在分配数组或结构体等数据结构时,使用calloc可以避免手动初始化的繁琐操作。
  6. 示例: 
    
    #include <stdio.h>
    #include <stdlib.h>
     
    int main() {
        int *ptr;
        int n = 5;
        // 使用 calloc 分配并初始化内存
        ptr = (int*)calloc(n, sizeof(int));
        if (ptr == NULL) {
            printf("Memory not allocated.\n");
            exit(1);
        }
        // 打印数组内容(应为0, 0, 0, 0, 0 因为 calloc 初始化了内存)
        for (int i = 0; i < n; i++) {
            printf("%d ", ptr[i]);
        }
        // 释放内存
        free(ptr);
        return 0;
    }
    
    ​

realloc

  1. 功能:用于重新分配之前通过malloc、calloc或realloc分配的内存块。它允许改变已分配内存块的大小,并根据需要调整内存块的位置。
  2. 初始化:不直接涉及初始化,因为它是对已分配内存的重新调整。但重新分配的内存部分(如果有的话)可能是未初始化的。
  3. 参数:两个参数,第一个参数是指向要重新分配的内存块的指针,第二个参数是要为新的内存块分配的字节数。
  4. 返回值:返回一个指向分配内存的指针,如果分配失败则返回NULL。
  5. 使用场景:适用于在已分配内存的基础上调整内存大小的情况,特别是在处理可变长数组或动态数据结构时非常有用。
  6. 示例:
    #include <stdio.h>
    #include <stdlib.h>
     
    int main() {
        int *ptr;
        int n = 5;
        // 初始分配内存
        ptr = (int*)malloc(n * sizeof(int));
        if (ptr == NULL) {
            printf("Memory not allocated.\n");
            exit(1);
        }
        // 初始化内存
        for (int i = 0; i < n; i++) {
            ptr[i] = i + 1;
        }
        // 打印原始数组内容
        printf("Original array: ");
        for (int i = 0; i < n; i++) {
            printf("%d ", ptr[i]);
        }
        // 使用 realloc 扩大内存
        n *= 2; // 扩大为原来的两倍
        ptr = (int*)realloc(ptr, n * sizeof(int));
        if (ptr == NULL) {
            printf("Memory not reallocated.\n");
            exit(1);
        }
        // 初始化新分配的内存部分
        for (int i = n / 2; i < n; i++) {
            ptr[i] = i + 1;
        }
        // 打印扩大后的数组内容
        printf("\nExpanded array: ");
        for (int i = 0; i < n; i++) {
            printf("%d ", ptr[i]);
        }
        // 释放内存
        free(ptr);
        return 0;
    }

C++中的内存管理

C语言内存管理方式在C++中可以继续使用,但有些地方就无能为力,而且使用起来比较麻烦,因此C++又提出了自己的内存管理方式:通过new和delete操作符进行动态内存管理。

new/delete操作符:

用于动态分配和释放单个对象的内存,会调用构造函数和析构函数。

new[]/delete[]操作符:

用于动态分配和释放数组的内存,同样会调用构造函数和析构函数。

operator new与operator delete函数
operator new 和 operator delete 是 C++ 中用于动态内存分配和释放的运算符函数。它们分别对应于 C 语言中的 malloc 和 free 函数,但提供了更多的灵活性和控制。

operator new

operator new 用于分配指定大小的内存块。它通常返回一个指向分配内存的指针,如果分配失败,则抛出 std::bad_alloc 异常。

自定义 operator new

你可以为自定义类重载 operator new,以提供特定的内存分配策略。例如:

#include <iostream>
#include <new> // for std::bad_alloc

class MyClass {
public:
    void* operator new(std::size_t size) {
        std::cout << "Custom new operator called, size: " << size << std::endl;
        void* p = malloc(size);
        if (!p) {
            throw std::bad_alloc();
        }
        return p;
    }

    void operator delete(void* p) noexcept {
        std::cout << "Custom delete operator called" << std::endl;
        free(p);
    }
};

int main() {
    try {
        MyClass* obj = new MyClass();
        delete obj;
    } catch (const std::bad_alloc& e) {
        std::cerr << "Memory allocation failed: " << e.what() << std::endl;
    }
    return 0;
}

 

operator delete

operator delete 用于释放由 operator new 分配的内存。它接受一个指向要释放内存的指针。

自定义 operator delete

与 operator new 类似,你也可以为自定义类重载 operator delete

#include <iostream>
#include <cstdlib> // for malloc and free
#include <new>     // for std::bad_alloc
 
// 自定义全局 operator delete
void operator delete(void* p) noexcept {
    std::cout << "Global delete operator called for void*\n";
    free(p);
}
 
// 自定义全局 operator delete[](数组形式)
void operator delete[](void* p) noexcept {
    std::cout << "Global delete[] operator called for void*\n";
    free(p);
}
 
class MyClass {
public:
    // 自定义成员级别的 operator delete
    void operator delete(void* p) noexcept {
        std::cout << "Member delete operator called for MyClass\n";
        ::operator delete(p); // 调用全局的 delete 运算符
    }
 
    // 自定义成员级别的 operator delete[](通常也需要定义,即使不使用数组)
    void operator delete[](void* p) noexcept {
        std::cout << "Member delete[] operator called for MyClass (not typically used for single objects)\n";
        ::operator delete[](p); // 调用全局的 delete[] 运算符
    }
 
    // 构造函数(为了完整性)
    MyClass() {
        std::cout << "MyClass constructor called\n";
    }
 
    // 析构函数(为了完整性)
    ~MyClass() {
        std::cout << "MyClass destructor called\n";
    }
};

new 运算符的实现原理

new 运算符在C++中用于动态分配内存并调用构造函数(如果适用)。它的实现通常分为以下几个步骤:

  1. 内存分配
    • new 运算符首先调用底层的内存分配函数(如 malloc 或 operator new),为对象分配足够的内存空间。
    • 如果内存分配失败(例如,由于内存不足),new 运算符将抛出一个 std::bad_alloc 异常。
  2. 构造对象
    • 在成功分配内存后,new 运算符调用对象的构造函数来初始化对象。
    • 对于基本数据类型(如 intfloat 等),这一步实际上不会执行任何操作,因为基本数据类型不需要构造函数。
  3. 返回指针
    • 最后,new 运算符返回指向新分配并初始化的对象的指针。

delete 运算符的实现原理

delete 运算符用于释放由 new 运算符分配的内存,并调用析构函数(如果适用)。它的实现通常分为以下几个步骤:

  1. 析构对象
    • delete 运算符首先调用对象的析构函数来执行必要的清理操作(如释放资源、关闭文件等)。
    • 对于基本数据类型,这一步同样不会执行任何操作。
  2. 释放内存
    • 在析构对象后,delete 运算符调用底层的内存释放函数(如 free 或 operator delete)来释放之前分配的内存空间。

 

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐