C++基础-指针使用注意
手动分配手动回收
程序在运行的时候需要内存,在c/c++中,栈上的内存(如函数中的局部非静态变量)在使用完之后,操作系统会帮我们自动回收,而通过动态分配得到的 **堆上的内存 **,需要手动释放。
平时写一些小程序可能我们不太注意这一点,因为用到的内存较少,在程序结束后,忘记释放的内存也会被强制回收。如果是编写大型的持续运行的程序,不注意内存释放,会导致内存占用越来越高,影响系统性能或导致进程崩溃。
下面一个测试程序演示内存占用情况:
1 |
|
直接运行(非调试)会有类似如下的正常结果:
1 | p = 0000021DEC181070 |
下面调试运行:
- 在cout语句行加断点,调试运行,同时打开电脑的任务管理器,运行程序后,可以在任务管理器中看到内存的占用突然增大(malloc的作用)而后回到正常(free的作用)。
- 而如果将free语句注释掉,再次调试运行至cout语句处,在任务管理器可以看到内存始终占用较多。如果此时在cout语句后还要大量代码需要分配内存,可能就会内存分配失败造成程序异常。
建议:使用内存分配函数分配内存时,注意malloc/free, new/delete成对使用。
警惕NULL指针
内存分配失败导致的NULL指针
上面程序malloc分配内存的大小需根据自己实际调整,如果太大会造成内存分配失败
直接运行(非调试)会有类似如下结果,程序没有正常结束:
1
2p = 0000000000000000
请按任意键继续. . .调试运行则会引发异常:
1
Exception thrown at 0x00007FF6216F5B96 in 指针.exe: 0xC0000005: Access violation writing location 0x0000000000000000.
原因在于内存分配失败,指针地址为0,即分配为空指针(NULL),给空指针写入内容时就会引发写入异常。
建议: 内存分配后,应使用if(p==NULL) 或if(p!=NULL)进行防错处理。
函数的指针参数传入NULL指针
含有指针参数的函数也有可能会误用到NULL指针:
1 |
|
调试运行,会引发读入异常:
1 | Exception thrown at 0x00007FF7BE145B74 in 指针.exe: 0xC0000005: Access violation reading location 0x0000000000000000. |
建议:函数中使用指针参数前,应使用if(p==NULL) 或if(p!=NULL)进行防错处理。
警惕野指针
野指针也叫悬挂指针,是指向“垃圾”内存的指针,使用“野指针”会让程序出现不确定的行为。 注意,野指针不是NULL指针, 它比NULL指针更容易犯错,因为它不能通过形如 if (NULL == p)的判断语句来预防,只能我们自己在写代码时多注意。
释放后忘记置NULL
指针p被free或者delete之后,没有置为NULL,让人误以为p是个合法的指针,事实上free或delete只是把指针所指的内存给释放掉,但是指针的值还是这块内存的地址(注:也可能是随机的地址,与编译器有关?在我的编译器上每次都是一个固定的其它值),只不过这块内存已经被回收了不能被该进程再使用。
1 |
|
直接运行(非调试),程序没有正常结束:
1
2
3p = 0000026D0FEF60B0
wild p = 0000000000008123
请按任意键继续. . .调试运行,会引发写入异常,因为p非空,但无法写操作:
1
Exception thrown at 0x00007FF64AC66BF5 in 指针.exe: 0xC0000005: Access violation writing location 0x0000000000008123.
取消上面p=NULL的注释,即可正常运行:
1
2
3
4
5p = 00000266C89166F0
wild p = 0000000000008123
null p = 0000000000000000
mian end
请按任意键继续. . .
建议:free或delete之后将相应的指针设置为NULL 。
指针定义后未初始化
指针定义后,在使用前,需要初始化,否则也是野指针,指向不确定:
1 |
|
我的编译器未编译通过:error C4700: uninitialized local variable ‘p’ used,可能某些编译器可以编译通过,进而引发程序异常。
建议:**定义指针变量的时候尽量初始化,哪怕初始化为NULL也好 **
不应返回局部变量的地址
c/c++中,局部变量是存放在栈中的,它的特点是随函数调用时创建随函数结束时销毁,因此在程序中将局部变量的地址返回后赋值给一个指针,这个指针指向的是一个已经被回收的内存,这也是一种野指针。
1 |
|
运行结果,p的值是随机数(每次运行都不一样):
1 | p = 0000001D686FF5C4 |
建议:不要在函数中返回局部变量的地址,如果必须返回局部变量的地址,则局部变量需申明为static类型(static变量的生存期是整个程序运行期间)
其它异常导致的内存无法释放
即使在malloc/new后显示调用了free/delete释放内存,但是由于其它异常可能会导致释放内存的free/delete语句得不到执行,也会发生内存泄露:
1 |
|
运行结果,无法进行delete:
1 | t->m_val100 |
类的析构函数没有被执行,可推知delete语句并没有得到执行。此程序在catch中加个delete 可解决问题,但对于一个庞大的工程时候,很难找出异常的位置。更好的解决方法是使用 智能指针。
建议:C++代码代码中多注意使用智能指针。