1.C++的内存分布
int value = 1;
static int staticvalue = 1;
void Test()
{
static int staval = 1;
int val = 1;
int num1 = {1,2,3,4};
char char2[] = "abcd";
char* char3 = "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.代码段
value 在哪里?C_____ staticvalue 在哪里?C____
staval 在哪里?C_____ val 在哪里?A______
num1 在哪里?A_____
char2在哪里?D____ * char2在哪里?A_____
char3在哪里?D____ *pChar3在哪里?A_____
ptr1在哪里?B____ *ptr1在哪里?A____
sizeof(num1) = 40___
sizeof(char2) = 5_
strlen(char2) = _4
sizeof(char3) = _4
strlen(char3) = ___4
sizeof(ptr1) = _4
注意:内存空间从上到下分为6个部分:内核空间,栈,内存映射段,堆,数据段,代码段
- 1.栈又叫堆栈,存储非静态局部变量 / 函数参数 / 返回值,栈是向下生长的。
- 2.内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库,用户可以使用系统接口创建共享内存,做进程间通信。(存储文件映射、动态库、匿名映射)
- 3.堆用于程序运行时动态内存分配,堆是向上增长的。
- 4.数据段存储全局数据和静态数据
- 5.代码段存储可执行的代码 / 只读常量
2.C语言中的动态内存管理方式
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(p3);
}
3.C++内存管理方式
1.new/ / delete申请和释放空间
void Test()
{
//动态申请一个int类型的空间
int* p4 = new int;
//动态申请一个int类型的空间并初始化为10
int* p5 = new int(10);
//动态申请5个int类型的空间
int* p6 = new int[5];
delete p4'
delete p5;
delete [] p6;
}
2.operator new 和 opreator delete 函数
注意:new和delete是用户进行动态内存申请和释放的操作符,
operator new和operator delete是系统提供的全局函数。
new在底层调用operator new全局函数来申请空间,delete在底层调用operator delete全局函数来释放空间
operator new:该函数实际通过malloc来释放空间,当malloc申请空间成功则返回;申请空间失败,则执行空间不足应对措施,如果改应对措施用户设置了,则继续申请,否则抛异常。
operator delete:最终是通过free来释放空间的。
4.new和delete的实现原理
1.内置类型
- 如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不同的地方:new/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申请空间失败后会抛出异常。
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来释放空间。
class Test()
{
public:
Test()
:_data(data)
{
std::cout << "Test()" << std::endl;
}
~Test()
{
std::cout << "~Test()" << std::endl;
}
private:
int _date;
}
int main()
{
//申请一个数据得空间
Test* p1 = new Test;
delete p1;
//申请10格数据的空间
Test* p2 = new Test[10];
delete [] p2;
system("pause");
return 0;
}
面试题
1.malloc / calloc / realloc 之间的区别?
答:malloc与calloc的区别为1块与n块的区别:
(1)malloc用于申请一段新的地址,参数size为需要内存空间的长度
(2)calloc与malloc相似,参数size为申请地址的单位元素长度,n为元素个数
(3)realloc是给一个已经分配了地址的指针重新分配空间,参数ptr为原有的空间地址,newsize是重新申请的地址长度
malloc调用形式为(类型*)malloc(size):在内存的动态存储区中分配一块长度为“size”字节的连续区域,返回该区域的首地址。
calloc调用形式为(类型*)calloc(n,size):在内存的动态存储区中分配n块长度为“size”字节的连续区域,返回首地址。
realloc调用形式为(类型*)realloc(*ptr,size):将ptr内存大小增大到size。
2.malloc / free 和new / delete的区别?
答:共同点:都是从堆上申请空间,并且需要用户手动释放
不同点:
- 1.malloc和free是函数,new和delete是操作符。
- 2.malloc申请的空间不会初始化,new可以初始化。
- 3.malloc申请空间时,需要手动计算大小并传递,new只需要在其后跟上空间的类型即可。
- 4.malloc的返回值为void*,在使用这个函数的时候必须强转,new不需要,因为new后面跟的是空间的类型
- 5.malloc在申请空间失败时,返回值为nullptr,因此使用的时候必须判空。new不需要,但是new必须捕获异常。
- 6.申请自定义类型对象的时候,malloc / free只会开辟空间,不会调用构造函数和析构函数,而new在申请空间的时候会调用构造函数完成对象的初始化,delete在释放空间的时候会调用析构函数完成空间中资源的清理。
- 7.new / delete比malloc / free的效率稍微低点,因为new / delete的底层封装了malloc / free
3.请设计一个类,该类只能在堆上创建对象
答:构造函数私有化
(1)将类的构造函数私有,拷贝构造声明为私有,防止别人调用拷贝构造在栈上生成空间。
(2)提供一个静态成员函数,在该静态成员函数中完成对象的创建。
class A
{
private :
A(){} //构造函数私有化
A(const A&); //拷贝构造函数私有
~A(){} //析构函数私有化
public :
static A* create()
{
return new A();
}
void destory()
{
delete this ;
}
};
//这样,调用create()函数在堆上创建类A对象,调用destory()函数释放内存。
4.请设计一个类,该类只能在栈上创建对象
只有使用new运算符,对象才会建立在堆上,因此,只要禁用new运算符就可以实现类对象只能建立在栈上。将operator new()设为私有即可。
代码如下:
class A
{
private :
void * operator new ( size_t t){} // 注意函数的第一个参数和返回值都是固定的
void operator delete ( void * ptr){} // 重载了new就需要重载delete
public :
A(){}
~A(){}
};
5.什么是内存泄漏?
答:内存泄漏是因为疏忽或者是错误造成程序未能释放已经不能再使用的情况,内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存的时候,因为设计错误,失去了对该段内存的控制,因此造成内存浪费。
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;
}
6.内存泄漏分类
- 1.堆内存分类
堆内存是指程序运行的时候要通过malloc / calloc / realloc / new等从堆中分配出一段内存,用完之后必须要调用相应的free和delete删掉。假设程序的设计错误导致这部分内存没有被释放,那么这部分空间将无法再被使用,就会产生Heap Leak. - 2.系统资源泄漏
指程序使用系统分配的资源,比如套接字,文件描述符,管道等的时候,没有使用对应的函数将其释放掉,导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定。
7.如何避免内存泄漏?
- 1.工程前期良好的设计规范,养成良好的编码规范,申请的空间记得去释放
ps:但是如果在释放之前有异常的话,就算释放了,也还会出现问题,那么就需要智能指针来进行管理。 - 2.采用RALL思想或者只能指针管理资源。
- 3.些公司内部规范使用内部实现的私有内存管理库。这套库自带内存泄漏检测的功能选项。
内存泄漏非常常见,解决方案分为两种:1、事前预防型。如智能指针等。2、事后查错型。如泄漏检测工 具。
8.如何一次在堆上申请4G的空间?
// 将程序编译成x64的进程,运行下面的程序试试?
#include <iostream>
using namespace std;
int main()
{
void* p = new char[0xfffffffful];
cout << "new:" << p << endl;
return 0;
}
8.如何在堆上申请4G的空间
如果是在x32的进行下,进行编译的话是申请不出来的
#include<iostream>
int main()
{
void* p = new char [0xffffffff];
std::cout << "new:" << p << std::endl;
return 0;
}
在x64平台下进行申请,可以申请出4G的空间 | 最大可以申请 = 4G * 4G;
9.x32和x64的区别?
-
32位的CPU(准确的说是运行在32位模式下的CPU)只能寻址最大4GB的内存,受制于此,32位的操作系统也只能识别最大4GB的内存,由于在系统中,除了内存之外,还有很多存储设备,因此,真正可以利用的内存空间肯定小于4GB,也就是我们看到的系统属性中显示的3.xxG。
-
但64位CPU则有了很大改变,64位CPU的最大寻址空间为2的64次方bytes,计算后其可寻址空间达到了惊人的16TB,即16384GB。当然,这只是理论,从实际应用上,Windows7 64bit的各版本分别为8GB-192GB,其中,家庭普通版能支持8GB内存,家庭高级版能支持16GB内存,而64位的Windows7专业版、企业版和旗舰版最高可支持192GB内存。
-
也就是说,内存大于等于4GB的用户,由于受到32位硬件限制,因此,不能最大化的利用硬件资源,这就很有必要安装64位操作系统以最大化利用资源。
-
需要注意的是,64位系统需要64位CPU的支持,在安装64位Win7前请先确认你的CPU是否支持64位,一般而言,2006年之后购买的CPU均支持64位模式。
-
如果仅考虑这点,对于内存小于4GB的用户,32位Win7更适合大家使用,因为64位版本会占用更多的系统内存资源,起的作用适得其反了
-
理论上,64位平台上的运行性能要远超过32位平台。原因在与CPU通用寄存器的数据位宽,64位平台是64位,而32位平台是32位,也就是说,64位平台可以运行64位数据指令,处理器一次可提取64位数据(只要两个指令,一次提取8个字节的数据),比32位(需要四个指令,一次提取4个字节的数据)提高了一倍,理论上性能会相应提升1倍。
-
但在64位Win7下运行32位的应用软件并不会让你感觉到性能的飞跃,只有64位的应用软件才能最大化发挥64位平台的优势。但显而易见,目前64位的应用程序在种类的数量上都要远低于32位平台,不过值得高兴的是,越来越多的软件开发者开始将他们的软件移植到64位平台上。