目录
c++中内存有四个区,分别是代码区,全局区,栈区,堆区
(1)代码区顾名思义就是内存中用来存放代码的区域,具有共享性(减少同时运行两次程序产生复制,造成不必要的资源浪费)和只读性(防止程序运行过程中代码被不经意修改)
(2)全局区用来存放全局变量、静态变量、常量、字符串常量、counst修饰的全局变量(全局常量)
(3)栈区用来存放局部变量,函数参数等,栈区数据的注意事项---不要返回局部变量的地址,栈区的数据由编译器管理和释放
(4)堆区是由开发人员决定的,由我们进行释放,若不释放程序结束后编译器自动释放,可以用new关键字进行开辟内存,用delete释放所开辟的内存
new关键字用于在堆区中申请内存时,有着一定的语法规则
//在堆区创建整型数据,就用整型指针接收
//new返回是该数据类型的指针#include
using namespace std;
int main()
{
int * p = new int((10));
float * b=new float((35.12f));
cout<<*p<
cout<<*b<
system("pause");
return 0;
}
分别输出int整型指针变量p的内容和float单精度浮点型指针变量b的内容
结果如下图所示:
可以正常输出
试着用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出来的数据
如果不对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关键字开辟出的是地址,既然是地址,就要用指针来接收,上一步验证过了要用同类型的对应指针型变量来接收,并且解指针后输出相应内容
那么直接用*解指针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的数据可行,但是为啥不常用呢,从个人角度来看,可读性不高且不够美观,所以不常用,习惯性用相应类型指针变量接收再解指针输出,显得更直观些
释放内存两种方式:
new <--> delete
new [] <--> delete []
对于普通变量,比如以下,可以在该变量之前加入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已经不属于你的私人财产范围了,你去染指,那就是超出了你的访问权限,属于非法访问,我们的编译器还是很公道的,不允许违法乱纪
如果我们按照释放普通变量(释放单个空间)的方法释放数组,会是怎样的?
- 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;
- }
new delete使用时注意以下几点
1.当传入的形参是数组时,能加const的就加const,防止在函数内部中的意外操作
2.内存new和delete时,原则:谁申请谁释放
3.在delete指针后,将指针置为NULL。例如:delete p; p = NULL;
这样可以避免出现以下现象:
1.执行delete语句时,程序直接弹窗,崩溃
2.执行delete语句时,程序卡死。将delete语句注释掉,又正常了,但发生了内存泄露