当前位置: 首页 > article >正文

C++ 复习总结记录五

C++ 复习总结记录五

C/C++ 内存管理主要内容

1、C/C++ 内存分布

2、C 中动态内存管理

3、C++ 中动态内存管理

4、operator new 与 operator delete 函数

5、new 和 delete 的实现原理

6、定位 new 表达式 (placement-new)

一 C/C++ 内存分布

int globalVar = 1;
static int staticGlobalVar = 1;
void Test()
{
    static int staticVar = 1;
    int localVar = 1;
    int num1[10] = { 1, 2, 3, 4 };
    char char2[] = "abcd";
    const char* pChar3 = "abcd";
    int* ptr1 = (int*)malloc(sizeof(int) * 4);
    int* ptr2 = (int*)calloc(4, sizeof(int));
    int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);
    free(ptr1);
    free(ptr3);
}

回答下列问题

1. 选择题:
   选项: A.栈  B.堆  C.数据段(静态区)  D.代码段(常量区)
   globalVar 在哪里		【C】
   staticGlobalVar 在哪里	【C】
   staticVar 在哪里		【C】
   localVar 在哪里			【A】
   num1 在哪里				【A】
   
   char2 在哪里			【A】
   *char2 在哪里			【A】
   pChar3 在哪里			【A】
   *pChar3 在哪里			【D】
   ptr1 在哪里				【A】
   *ptr1 在哪里			【B】

2. 填空题:
   sizeof(num1) =			40
   sizeof(char2) =			5
   strlen(char2) =			4
   sizeof(pChar3) = 		4(32 bit)
   strlen(pChar3) = 		4
   sizeof(ptr1) = 			4(32 bit)
   
3. sizeof 和 strlen 区别
sizeof 是一个运算符,而 strlen 是一个函数
sizeof 计算的是变量或类型所占用的内存字节数,而 strlen 计算的是字符串中字符的个数
sizeof 可以用于任何类型的数据,而 strlen 只能用于以空字符 '\0' 结尾的字符串
sizeof 计算字符串的长度,包含末尾的 '\0',strlen 计算字符串的长度,不包含字符串末尾的 '\0'

image-20250108212307376

【说明】

1、栈 – 非静态局部变量 / 函数参数 / 返回值等等,向下增长

2、内存映射段 – 是高效 I/O 映射方式,用于装载一个共享动态内存库。也可使用系统接口创建共享共享内存,做进程间通信

3、堆 – 用于程序运行时动态内存分配,向上增长

4、数据段 – 存储全局数据和静态数据

5、代码段 – 可执行代码 / 只读常量

二 C 中动态内存管理

C 中动态内存管理方式:malloc / calloc / realloc / free

void Test ()
{
    int* p1 = (int*) malloc(sizeof(int));
    free(p1);
    int* p2 = (int*)calloc(4, sizeof (int));
    int* p3 = (int*)realloc(p2, sizeof(int)*10);
    // 这里不需要 free(p2)
    free(p3); 
}

malloc / calloc / realloc 区别

calloc 会对申请空间初始化,并且初始化为 0;malloc申请的空间则一般使用 memset 初始化;realloc 对已存在的空间进行调整,当第一个参数传入 NULL 时和malloc 一样

malloc 实现原理

三 C++ 中动态内存管理

C 内存管理 C++ 兼容,且 C++ 有自己的动态内存管理:通过 new 和 delete 操作符

3.1 new / delete 操作内置类型

void Test()
{
    // 动态申请一个int类型的空间
    int* ptr4 = new int;

    // 动态申请一个int类型的空间并初始化为 10
    int* ptr5 = new int(10);

    // 动态申请 10 个int类型的空间
    int* ptr6 = new int[3];
    delete ptr4;
    delete ptr5;
    delete[] ptr6;
}

注意:申请和释放单个元素空间,使用 new 和 delete,申请和释放连续空间,使用 new[] 和 delete[]

image-20250108213714482

3.2 new 和 delete 操作自定义类型

class A
{
public:
    A(int a = 0)
        : _a(a)
        {
            cout << "A():" << this << endl;
        }
    ~A()
    {
        cout << "~A():" << this << endl;
    }
    
private:
    int _a;
};


int main()
{
    // new/delete 和 malloc/free 最大区别是 new/delete对于[自定义类型]除了开空间还会调用构造函数和析构函数
    A* p1 = (A*)malloc(sizeof(A));
    A* p2 = new A(1);
    free(p1);
    delete p2;
    // 内置类型是几乎一样
    int* p3 = (int*)malloc(sizeof(int)); // C
    int* p4 = new int;
    free(p3);
    delete p4;
    A* p5 = (A*)malloc(sizeof(A)*10);
    A* p6 = new A[10];
    free(p5);
    delete[] p6;
    return 0;
}

在申请自定义类型空间时,new 会调用构造函数,delete 会调用析构函数,而 malloc 与 free 不会

四 operator new 与 operator delete 函数

4.1 operator new 与 operator delete 函数(重点)

new 和 delete 是 C++ 进行动态内存申请和释放的操作符,new 底层调用 operator new 全局函数来申请空间,delete 底层通过 operator delete 全局函数来释放空间

/*
operator new : 函数实际通过 malloc 申请空间, 当 malloc 申请空间成功时直接返回; 申请空间失败, 尝试执行空间不足应对措施, 如果应对措施设置了, 则继续申请, 否则抛异常
*/
void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{
    // try to allocate size bytes
    void *p;
    while ((p = malloc(size)) == 0)
        if (_callnewh(size) == 0)
        {
            // report no memory
            // 如果申请内存失败了,这里会抛出bad_alloc 类型异常
            static const std::bad_alloc nomem;
            _RAISE(nomem);
        }
    return (p);
}

/*
operator delete: 函数最终通过 _free_dbg 来释放空间
*/
void operator delete(void *pUserData)
{
    _CrtMemBlockHeader * pHead;
    RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));
    if (pUserData == NULL)
        return;
    _mlock(_HEAP_LOCK);  /* block other threads */
    __TRY
        /* get a pointer to memory block header */
        pHead = pHdr(pUserData);
    /* verify block type */
    _ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
    _free_dbg( pUserData, pHead->nBlockUse );
    __FINALLY
        _munlock(_HEAP_LOCK);  /* release other threads */
    __END_TRY_FINALLY
        return;
}

/*
free的实现
*/
#define   free(p)               _free_dbg(p, _NORMAL_BLOCK)

operator new 实际通过 malloc 申请空间,如果 malloc 申请空间成功则直接返回,失败则抛异常;operator delete 通过 _free_dbg 释放空间

五 new 和 delete 的实现原理

5.1 内置类型

申请内置类型空间,new 和 delete 与 malloc 和 free 类似

不同是 new 在申请空间失败时会抛异常,malloc会返回NULL;new / delete 申请和释放的是单个元素的空间,new[] 和 delete[] 申请的是连续空间

5.2 自定义类型

new 原理

1、调用 operator new 申请空间

2、在申请的空间上执行构造函数,完成对象构造

delete 原理

1、在空间上执行析构函数,完成对象中资源清理

2、调用 operator delete 释放对象空间

new T[N] 原理

1、调用 operator new[] 函数,在 operator new[] 中实际调用 operator new 函数完成 N 个对象空间申请

2、在申请空间上执行 N 次构造函数

delete[] 原理

1、在对象空间上执行 N 次析构函数,完成 N 个对象中资源清理

2、调用 operator delete[] 释放空间,在 operator delete[] 中实际调用operator delete 释放空间

六 定位 new 表达式 (placement-new)

定位 new 表达式是在已分配的内存空间中调用构造函数初始化一个对象

使用格式:

new (place_address) type 或者 new (place_address) type(initializer-list)

place_address 必须是一个指针,initializer-list 是类型的初始化列表

使用场景

定位 new 表达式一般配合内存池使用,内存池分配出的内存未初始化,自定义类型的对象需要使用 new 定义表达式进行显示调构造函数初始化

class A
{
    public:
    A(int a = 0)
        : _a(a)
        {
            cout << "A():" << this << endl;
        }
    ~A()
    {
        cout << "~A():" << this << endl;
    }
    private:
    int _a;
};

// 定位new/replacement new
int main()
{
    // p1 指向与 A 对象相同大小的一段空间, 但还不算是一个对象, 因为构造函数没有执行
    A* p1 = (A*)malloc(sizeof(A));
    new(p1)A;  // 注意: 如果 A 类构造函数有参数时, 此处需要传参
    p1->~A();
    free(p1);
    A* p2 = (A*)operator new(sizeof(A));
    new(p2)A(10);
    p2->~A();
    operator delete(p2);
    return 0;
}

七 练习题

7.1 malloc / free 和 new / delete 区别

new 会调用构造函数,失败抛异常,malloc 失败返回 NULL

malloc 是一个函数,new 是一个操作符

malloc 用法,参数传字节数,返回值 void*;new 后跟申请对象类型,返回值是类型指针

7.2 内存泄漏

因错误地造成程序未能释放已经不再使用的内存的情况叫做内存泄漏

内存泄漏危害:出现内存泄漏导致程序响应越来越慢,或无可利用资源

void MemoryLeaks()
{
   // 1.内存申请后忘记释放
  int* p1 = (int*)malloc(sizeof(int));
  int* p2 = new int;
  
  // 2.异常安全问题
  int* p3 = new int[10];
  Func(); // Func 函数抛异常导致 delete[] p3 未执行, p3 没被释放
  delete[] p3;
}

7.3 检测内存泄漏

VS 下可以使用 windows 操作系统提供的 _CrtDumpMemoryLeaks() 函数简单检测,该函数只报出了大概泄漏了多少个字节,没有更准确的位置信息

int main()
{
    int* p = new int[10];
    // 将该函数放在 main 函数之后, 程序退出时就会检测是否存在内存泄漏
    _CrtDumpMemoryLeaks();
    return 0;
}

// 程序退出后, 在输出窗口可以检测到泄漏了多少字节, 但是没有具体位置
Detected memory leaks!
    Dumping objects ->
{79} normal block at 0x00EC5FB8, 40 bytes long.
    Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
        Object dump complete.

工程比较大,一般借助第三方内存泄漏检测工具处理

在 linux 下内存泄漏检测:linux 下几款内存泄漏检测工具

在windows下使用第三方工具:VLD 工具说明

其它工具:内存泄漏工具比较

7.4 避免内存泄漏

1、良好编码规范,申请内存空间需要对应的释放

2、采用 RAII 思想或智能指针来管理资源

3、使用内存泄漏工具检测

如何一次在堆上申请 4G 内存

image-20250108222420222

设置网页缩放大小

image-20250108172325533
设置 typora 字体大小

image-20250108172354162


http://www.kler.cn/a/472573.html

相关文章:

  • 【Vue】:解决动态更新 <video> 标签 src 属性后视频未刷新的问题
  • Android 来电白名单 只允许联系人呼入电话
  • 文献综述拆解分析
  • 25上软考中级【软件设计师】易混淆知识点
  • 计算机网络之---TCP/IP四层模型
  • 【网络协议】IPv4 地址分配 - 第二部分
  • (k8s)kubectl不断重启问题解决!
  • 代码随想录算法训练营第二十七天-贪心算法-455. 分发饼干
  • 技术速递|通过 .NET Aspire 使用本地 AI 模型
  • 【大模型+本地自建知识图谱/GraphRAG/neo4j/ollama+Qwen千问(或llama3)】 python实战(中)
  • 支持各大平台账单处理,支持复杂业财数据的精细化对账|商派OMS
  • 将java前后端项目和使用了conda虚拟环境的python项目添加到ubuntu服务
  • python中的列表推导式详解
  • 华灯已上:夜色跌宕绘情谱
  • 【AI日记】25.01.08
  • PLC实现HTTP协议JSON格式数据上报对接的参数配置说明
  • OBS Zoom to Mouse 脚本安装与使用指南
  • MySQL UDF提权
  • 1-【选修】逻辑回归
  • 2025新春烟花代码(二)HTML实现孔明灯和烟花效果
  • SpringBoot 使用 Cache 集成 Redis做缓存保姆教程
  • 能不能在家部署一个硬件实现远程唤醒局域网内所有电脑?
  • 从零手写实现redis(三)内存数据如何重启不丢失?
  • Spring Boot 项目自定义加解密实现配置文件的加密
  • ceph集群配置
  • IDEA的常用设置