C++ ─── 内存管理

1 . 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";

	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在哪里?____  staticGlobalVar在哪里?____

  staticVar在哪里?____  localVar在哪里?____

  num1 在哪里?____

  

  char2在哪里?____	  *char2在哪里?___

  pChar3在哪里?____   *pChar3在哪里?____

  ptr1在哪里?____    *ptr1在哪里?____



2. 填空题:

  sizeof(num1) = ____;  

  sizeof(char2) = ____;   strlen(char2) = ____;

  sizeof(pChar3) = ____;   strlen(pChar3) = ____;

  sizeof(ptr1) = ____;

1. 选择题:

          选项: A.栈 B.堆 C.数据段(静态区) D.代码段(常量区)

          globalVar在哪里?__C__  staticGlobalVar在哪里?__C__

          staticVar在哪里?__C__  localVar在哪里?__A__

          num1 在哪里?__A__

  分析:

          globalVar全局变量在数据段 staticGlobalVar静态全局变量在静态区

          staticVar静态局部变量在静态区  localVar局部变量在栈区

          num1局部变量在栈区

          char2在哪里?__A__  *char2在哪里?__A__

          pChar3在哪里?__A__   *pChar3在哪里?__D__

          ptr1在哪里?__A__    *ptr1在哪里?__B__

  分析:

          char2局部变量在栈区  

          char2是一个数组,把后面常量串拷贝过来到数组中,数组在栈上,所以*char2在栈上

          pChar3局部变量在栈区   *pChar3得到的是字符串常量字符在代码段

          ptr1局部变量在栈区     *ptr1得到的是动态申请空间的数据在堆区

2. 填空题:

          sizeof(num1) = __40__;//数组大小,10个整形数据一共40字节

          sizeof(char2) = __5__;//包括\0的空间

          strlen(char2) = __4__;//不包括\0的长度

          sizeof(pChar3) = __4__;//pChar3为指针

          strlen(pChar3) = __4__;//字符串“abcd”的长度,不包括\0的长度

          sizeof(ptr1) = __4__;//ptr1是指针

【说明】

        1. 又叫堆栈--非静态局部变量/函数参数/返回值等等,栈是向下增长的,栈底地址高,栈顶的地址低

        2. 内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存,做进程间通信。(Linux课程如果没学到这块,现在只需要了解一下)

        3.用于程序运行时动态内存分配,堆是可以上增长的

        4. 数据段--存储全局数据静态数据

        5. 代码段--可执行的代码/只读常量

.

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

void Test ()
{
 int* p1 = (int*) malloc(sizeof(int));
 free(p1);
 
 // 1.malloc/calloc/realloc的区别是什么?
 int* p2 = (int*)calloc(4, sizeof (int));
 int* p3 = (int*)realloc(p2, sizeof(int)*10);
 
 // 这里需要free(p2)吗?
 free(p3 );
}

【面试题】

1. malloc/calloc/realloc的区别?

2. malloc的实现原理?【CTF】GLibc堆利用入门-机制介绍_哔哩哔哩_bilibili

3. 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[10];
 
 delete ptr4;
 delete ptr5;
 delete[] ptr6;
}

        注意:申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用new[]和 delete[],注意:匹配起来使用

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);
    //可以进行初始化,本质是1进行对类型A进行隐式类型转化产生的临时对象
     A* p6 = new A[10]{1,2,3,4};
     free(p5);
     delete[] p6;

    //多参数的隐式类型转换
    Date* p8=new Date[6]{{2024,4,26},{2024,5,1}};
    delete[] p8;
 
     return 0;
}

        注意:在申请自定义类型的空间时,new=opeartor new+构造函数,delete=析构函数+operator delete,而malloc与free不会。

        下面有关malloc和new,说法错误的是? ( )

A.new 是创建一个对象(先分配空间,再调构造函数初始化), malloc分配的是一块内存

B.new 初始化对象,调用对象的构造函数,对应的delete调用相应的析构函数,malloc仅仅分配内存,free仅仅回收内存

C.new和malloc都是保留字,不需要头文件支持

D.new和malloc都可用于申请动态内存,new是一个操作符,malloc是是一个函数

A.new会申请空间,同时调用构造函数初始化对象,malloc只做一件事就是申请空间

B.new/delete与malloc/free最大区别就在于是否会调用构造函数与析构函数

C.需要头文件malloc.h,只是平时这个头文件已经被其他头文件所包含了,用的时候很少单独引入,故错误

D.new是操作符,malloc是函数

4. operator new与operator delete函数(重要点进行讲解)

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

.

        new和delete是用户进行动态内存申请和释放的操作符,operator new 和operator delete是系统提供的 全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局 函数来释放空间。

        下面的代码,我们看不太懂,我们只需要理解operator new是对malloc的封装,加入了失败会抛异常的功能,而operator delete 是对free的封装。

/*
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来释放空间的
*/
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来释放空间的。

5. new和delete的实现原理(以上总结)

5.1 内置类型

        如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不同的地方是:new/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申请空间失败时会抛异常, malloc会返回NULL

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来释放空间

例题一:

        使用 char* p = new char[100]申请一段内存,然后使用delete p释放,有什么问题?( )

A.会有内存泄露

B.不会有内存泄露,但不建议用

C.编译就会报错,必须使用delete []p

D.编译没问题,运行会直接崩溃

A.对于内置类型,此时delete就相当于free,因此不会造成内存泄漏

B.正确

C.编译不会报错,建议针对数组释放使用delete[],如果是自定义类型,不使用方括号就会运行时错误,因为析构函数调用数量不够。

D.对于内置类型,程序不会崩溃,但不建议这样使用

例题二:

ClassA *pclassa=new ClassA[5];

delete pclassa;

c++语言中,类ClassA的构造函数和析构函数的执行次数分别为( )

A.5,1

B.1,1

C.5,5

D.程序可能崩溃

A.申请对象数组,会调用构造函数5次,delete由于没有使用[],此时只会调用一次析构函数,但往往会引发程序崩溃

B.构造函数会调用5次

C.析构函数此时只会调用1次,要想完整释放数组空间,需要使用[]

D.正确

相关推荐

  1. C/C++——内存管理

    2024-04-27 23:28:02       65 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-04-27 23:28:02       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-04-27 23:28:02       106 阅读
  3. 在Django里面运行非项目文件

    2024-04-27 23:28:02       87 阅读
  4. Python语言-面向对象

    2024-04-27 23:28:02       96 阅读

热门阅读

  1. 目前还能使用的免费证书

    2024-04-27 23:28:02       28 阅读
  2. SIC知识(8)--碳化硅的制备难点

    2024-04-27 23:28:02       28 阅读
  3. 诡异的linux系统负载问题

    2024-04-27 23:28:02       34 阅读
  4. Swift手撸轮播效果

    2024-04-27 23:28:02       28 阅读
  5. 001 rabbitmq减库存demo direct

    2024-04-27 23:28:02       31 阅读
  6. 【C++】使用 std::shared_ptr 导致的循环引用问题

    2024-04-27 23:28:02       30 阅读
  7. mysql 连接数配置,解决Too many connections错误

    2024-04-27 23:28:02       27 阅读
  8. 数据结构中的栈和队列(附实际案例代码)

    2024-04-27 23:28:02       26 阅读