1.内存分布示意图(重要)
其中数据共享区也叫内存映射段,是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存,做进程间通信。
2.判断以下程序中的变量在什么区域
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); }
- 选择题: 选项: A.栈 B.堆 C.数据段(静态区) D.代码段(常量区) globalVar在哪里?C staticGlobalVa在哪里?C staticVar在哪里?_C localVar在哪里?A
num1 在哪里?A
char2在哪里?A *char2在哪里?A pChar3在哪里?A *pChar3在哪里?D ptr1在哪里?A *ptr1在 哪里?B
- 填空题: sizeof(num1) = 40; (sizezof 数组名等于整个数组的大小) sizeof(char2) = 5__; strlen(char2) = 4; sizeof(pChar3) =
4; strlen(pChar3) = 4; sizeof(ptr1) = 4;
- sizeof 和 strlen 区别? 答:sizeof 计算的是字节的大小,sizezof 数组名等于整个数组的大小,strlen计算字符串的长度,遇到‘\0’为止
3.new和delete
new和delete是C++动态开辟与释放内存互相匹配的的两个操作符,因此不需要头文件。C语言内存管理方式在C++中可以继续使用,但有些地方就无能为力,而且使用起来比较麻烦,因此C++又提出了自己的内存管理方式:通过new和delete操作符进行动态内存管理。
本质上,new的底层也是通过malloc来实现的
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[],使用的时候要互相匹配。
4.malloc和new的区别
1.属性:new是关键字,而malloc是函数。
2.参数:new操作符会无需指定内存块的大小,编译器会自己根据类型算,而malloc需要显式的指定。
3.返回值类型:newc操作符申请成功时返回该对象类型的指针,无需强制转换,申请失败的时候会报异常。而malloc函数申请成功返回的时void*类型的指针,大多需要强制类型转换,申请失败会返回一个NULL.
4.new可以调用malloc来实现,但是malloc不可以。
5.对于自定义类型开辟空间
对于内置类型,new与malloc几乎没有什么区别,但是对于自定义类型,也就是class,new和delete会调用构造函数和析构函数.
5.delete和delete[]的区别
delete 释放new分配的单个对象指针指向的内存;
delete[] 释放new分配的对象数组指针指向的内存。
对于内置类型:
delete和delete[]的效果是一样的
void test2() { int* a = new int[4]; int* b = new int; delete[] a; delete b; }
因为对于内置类型,在new分配内存的时候就已经确定了内存的大小,并且记录了下来, 系统可以记忆并且进行管理,在析构时,系统并不会调用析构函数。
对于自定义类型:
void test3() { A* p1 = new A[5]; //delete p1;错误 delete[] p1; }
delete与delete[]的效果是不一样的,delete只会调用一次析构函数,而delete[]会调用多次。那么delete[]是如何知道需要调用多少次析构函数呢?也就是如何知道对象数组的元素个数呢?如果我们开辟了用new []开辟了对象数组,编译器会在数组的前面再开辟一块空间用来存放元素个数。
此时如果用delete[]释放空间,它会先从上一块区域(32位系统是4字节,64位是8个字节)读取元素个数并释放。
这也是为什么用delete 释放对象数组会报错误,因为deletet是从指针p1指向的位置开始,但是实际上p1上一面的内存块也要释放。动态内存不能只释放一部分。
5.内存泄漏
内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误失去了对该段内存的控制,因而造成了内存的浪费。
内存泄漏的危害:长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现内存泄漏会导致响应越来越慢,最终卡死。
内存泄漏的分类:
堆内存泄漏(heap leak):
堆内存泄漏是指程序中通过new/malloc等申请到的堆空间没有及时用delete/free释放掉,导致这片空间无法再被申请。
系统内存泄漏
指程序使用系统分配的资源,比方套接字、文件描述符、管道等没有使用对应的函数释放掉,导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定。
_CrtDumpMemoryLeaks() 函数
crtdbg模块的c++_CrtDumpMemoryLeaks函数可以检测C、C++代码中的内存泄漏错误。在需要检测的代码前后分别调用_CrtDumpMemoryLeaks函数,就可以看到该代码片段是否发送内存泄漏,以及泄露的空间大小,但是不能提供泄漏的具体地址。注意,要在进入调试窗口才能看到该信息。
如何避免内存泄漏
- 工程前期良好的设计规范,养成良好的编码规范,申请的内存空间记着匹配的去释放。
- 采用RAII思想或者智能指针来管理资源。
- 有些公司内部规范使用内部实现的私有内存管理库。这套库自带内存泄漏检测的功能选项。
- 使用内存泄漏工具检测。
总结:
1、事前预防型。如智能指针等。2、事后查错型。如泄漏检测工具。