动态内存管理面试夺命六连问
1 程序运行起来后,内存被分为几个区域?操作系统为什么要对内存分这些区域?
答:程序运行起来后,内存被分为内核态和用户态,内核态控制操作系统的主要运行,而用户态是用户自己定义实现的。其主要分布如下:
- 内核空间:放操作系统的代码以及数据的,只要操作系统能够进行访问的话,必须通过专门的API函数来进行访问。
- 栈:存放和函数调用相关的数据,比如局部变量,参数,一些寄存器的信息。
- 内存映射段:是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存,做进程间通信。
- 堆:程序员动态申请的空间,堆是向上增长的。
- 数据段:全局变量和static修饰的变量。
- 代码段:该部分的内容是不允许被修改的代码和常量。
通俗的理解是不同的区域,功能是不一样的,方便操作系统管理这些数据,比如房子中客厅是休息的,卧室是睡觉的,厨房是做饭的,卫生间是上厕所的,阳台是晾衣服的等。
2 什么是动态内存?
答:动态内存是用户自己申请的内容空间,并且在用完之后,是需要由用户动态进行释放的空间。
3 为什么需要进行动态内存申请呢?
答:在内存不同的区域存放不同的是不同的数据,而用户数据能够存放的位置是在堆和栈上,栈是有固定大小的,这就导致用户的数据可能非常大,在栈区中存储不下时,需要更大的内存空间类存储用户的数据,而更大的内存空间用户需要向操作系统来进行获取。栈空间在函数运行时的栈帧随函数运行而创建,随函数的结束而回收,回收之后函数中局部变量的数据就没有意义,但是有些情况下,我们需要将程序运行期间产生的数据带出函数之外,这就需要进行动态内存申请。
4 动态内存申请时,需要注意什么问题?
答:一般在内存申请的时候需要多少申请多少,在使用时,需要越界,用完之后记得及时归还。
5 C语言中是如何进行内存申请的?
- 必须要包含对应的头文件。
- 使用动态内存申请的函数:malloc/calloc/realloc。
- 空间使用完成之后一定要归还:free()。
5.1 malloc/calloc/realloc有什么区别?
5.1.1 相同点
- 都是c语言标准的库函数;
- 使用的都是必须包含对应的头文件;
- 都是用来进行动态内存空间申请的;
- 申请的空间都是在堆上;
- 申请空间成功后返回空间的首地址,申请空间失败后返回的是NULL,因此在使用之前必须进行判空;
- 他们的返回值类型都是void*,在使用时必须要进行强制类型转换;
- 用完之后一定要用free来进行释放。
5.1.2不同点
void* malloc(size_t size) ;
- 参数是所有需要空间的字节数;
- 返回值是void*,使用时必须要进行强制类型转换;
- 申请成功,返回空间的首地址,申请失败返回NULL,因此在使用时,必须要进行判空;
- 使用完成后,一定要使用free来进行释放。
void* calloc(size_t num, size_t size);
- num表示参数的个数,size表示单个元素的大小;
- calloc会对申请的空间使用0来初始化。
void* realloc(void* ptr, size_t size);
主要功能:将ptr指向的空间重新调整到size个字节,完成之后返回空间的首地址,返回值与一些其他的信息(比如判空,释放等)与malloc相同。
- 如果ptrs是空:功能与malloc类似,直接申请size个字节返回即可;
- 如果ptr非空:将ptr指向的空间重新调整到size字节,完成后返回空间的首地址。
6 C++为什么要再给自己的一套动态内存申请的方式呢?
因为malloc和free是C语言中标准的库函数,但C语言内存管理方式在C++可以继续使用,但有些地方就无能为力而且使用起来比较麻烦,并且malloc在创建对象类型空间期间不会调用构造函数。free在释放对象类型空间期间不会调用析构函数。因此C++提出了自己的内存管理方式,通过new和delete操作符进行动态内存管理。