目录
1.堆区概念
c++中内存有四个区,分别是代码区,全局区,栈区,堆区
(1)代码区顾名思义就是内存中用来存放代码的区域,具有共享性(减少同时运行两次程序产生复制,造成不必要的资源浪费)和只读性(防止程序运行过程中代码被不经意修改)
(2)全局区用来存放全局变量、静态变量、常量、字符串常量、counst修饰的全局变量(全局常量)
(3)栈区用来存放局部变量,函数参数等,栈区数据的注意事项---不要返回局部变量的地址,栈区的数据由编译器管理和释放
(4)堆区是由开发人员决定的,由我们进行释放,若不释放程序结束后编译器自动释放,可以用new关键字进行开辟内存,用delete释放所开辟的内存
2.new的基本语法
2.1相应类型指针变量进行接收
new关键字用于在堆区中申请内存时,有着一定的语法规则
//在堆区创建整型数据,就用整型指针接收
//new返回是该数据类型的指针#include<iostream>
using namespace std;
int main()
{
int * p = new int((10));
float * b=new float((35.12f));
cout<<*p<<endl;
cout<<*b<<endl;
system("pause");
return 0;
}
分别输出int整型指针变量p的内容和float单精度浮点型指针变量b的内容
结果如下图所示:
可以正常输出
2.2非对应类型指针变量进行接收(会报错)
试着用float类型指针接收开辟的int内存
int main()
{
float * p = new int((10));
int * b = new float((35.12f));
cout << *p << endl;
cout << *b << endl;
system("pause");
return 0;
}
产生了错误,提示是:int 类型的值不能用于初始化float类型的实体
从这个报错内容可以看出:01.new返回的同类型的指针,要用同类型指针接收new出来的数据
2.3不用指针变量接收(结果?)
如果不对new的内容进行接收,能输出么?
int main()
{
cout << "没有进行接收的new数据输出"<< new int((10)) << endl;
int * p = new int((10));
float * b = new float((35.12f));
cout << *p << endl;
cout << *b << endl;
system("pause");
return 0;
}
输出结果是一串地址,也间接说明了new关键字开辟出的是地址,既然是地址,就要用指针来接收,上一步验证过了要用同类型的对应指针型变量来接收,并且解指针后输出相应内容
2.4直接解指针输出new内容(结果?)
那么直接用*解指针new出的内容呢?
int main()
{
/*int *p = func();
test01();
test02();*/
cout << "没有进行接收的new数据输出"<< new int((10)) << endl;
cout << "解指针new数据输出" << *(new int((10))) << endl;
int * p = new int((10));
float * b = new float((35.12f));
cout << *p << endl;
cout << *b << endl;
system("pause");
return 0;
}
从输出结果与之后的用指针型变量来接收的结果来看,是一样的,所以,直接输出解指针new的数据可行,但是为啥不常用呢,从个人角度来看,可读性不高且不够美观,所以不常用,习惯性用相应类型指针变量接收再解指针输出,显得更直观些
3.delete释放new开辟出的内存
释放内存两种方式:
new <--> delete
new [] <--> delete []
3.1delete在普通变量中的的用法(释放单个空间)
对于普通变量,比如以下,可以在该变量之前加入delete关键字直接进行释放
p 是指向动态分配的内存的指针
int main()
{
int *p = new int(10);
cout << *p << endl;
delete p;//释放
cout << *p << endl;//释放了之后能正常输出么?
system("pause");
return 0;
}
引发了异常,访问权限
换句直白点的比喻就是说,你手上有100REN,拿去吃了顿饭,把100REN给了饭店老板,相当于这个100REN已经被你释放了,时候再问老板要,人家会给你么,这就产生了100REN的归属权,释放了100REN之后,那个100REN已经不属于你的私人财产范围了,你去染指,那就是超出了你的访问权限,属于非法访问,我们的编译器还是很公道的,不允许违法乱纪
3.2delete释放数组的用法(释放多个连续空间)
如果我们按照释放普通变量(释放单个空间)的方法释放数组,会是怎样的?
int main()
{
int * arr = new int[10];//new一个10个元素的数组arr,用int类型指针arr接收
for (int i = 0; i < 10; i++)
{
arr[i] = i + 100;//给每个数组元素赋值,范围是100~109
}
//输出arr[i]
for (int i = 0; i < 10; i++)
{
cout << arr[i] << endl;
}
delete arr;//按照3.1方法释放数组
system("pause");
return 0;
}
结果如下,虽然可以正常输出,但其实数组并没有被释放
如果动态分配了一个数组,但是却用
delete p
的方式释放,没有用[]
,则编译时没有问题,运行时也一般不会发生错误,但实际上会导致动态分配的数组没有被完全释放。
牢记,用 new 运算符动态分配的内存空间,一定要用 delete 运算符释放。否则,即便程序运行结束,这部分内存空间仍然不会被操作系统收回,从而成为被白白浪费掉的内存垃圾。这种现象也称为“内存泄露”。
如果一个程序不停地进行动态内存分配而总是没有释放,那么可用内存就会被该程序大量消耗,即便该程序结束也不能恢复。这就会导致操作系统运行速度变慢,甚至无法再启动新的程序。但是,只要重新启动计算机,这种情况就会消失。
编程时如果进行了动态内存分配,那么一定要确保其后的每一条执行路径都能释放它。
另外还要注意,释放一个指针,并不会使该指针的值变为 NULL。
在释放new数组时,应该告诉编译器释放的是数组类型的arr,一一对应,不仅能达到真正的释放多个连续空间,也能增加代码健壮性
在arr 前面加[]中括号
int main()
{
int * arr = new int[10];//new一个10个元素的数组arr,用int类型指针arr接收
for (int i = 0; i < 10; i++)
{
arr[i] = i + 100;//给每个数组元素赋值,范围是100~109
}
//输出arr[i]
for (int i = 0; i < 10; i++)
{
cout << arr[i] << endl;
}
//delete arr;
delete[] arr;
arr=NULL;//把释放后的指针置为NULL,这里不用加中括号
system("pause");
return 0;
}
4.总结和注意事项
new delete使用时注意以下几点
1.当传入的形参是数组时,能加const的就加const,防止在函数内部中的意外操作
2.内存new和delete时,原则:谁申请谁释放
3.在delete指针后,将指针置为NULL。例如:delete p; p = NULL;
这样可以避免出现以下现象:
1.执行delete语句时,程序直接弹窗,崩溃
2.执行delete语句时,程序卡死。将delete语句注释掉,又正常了,但发生了内存泄露