第五章、内存管理
- 为防止野指针,将指针初始化和使用后为NULL。
- 栈、队和静态区
静态区:保存自动全局变量和static 变量(包括static 全局和局部变量)。静态区的内容
在总个程序的生命周期内都存在,由编译器在编译的时候分配。
栈:保存局部变量。栈上的内容只在函数的范围内存在,当函数运行结束,这些内容
也会自动被销毁。其特点是效率高,但空间大小有限。
堆:由malloc 系列函数或new 操作符分配的内存。其生命周期由free 或delete 决定。
在没有释放之前一直存在,直到程序结束。其特点是使用灵活,空间比较大,但容易出错。 - 指针没有指向一篇合法内存的常见错误:
①结构体成员指针未初始化;
②没有为结构体指针分配足够的内存空间;
③函数的入口检验(不管什么时候,我们使用指针之前一定要确保指针是有效的。)
一般在函数入口处使用assert(NULL != p)对参数进行校验。在非参数的地方使用
if(NULL != p)来校验。但这都有一个要求,即p 在定义的同时被初始化为NULL 了。 - assert 是一个宏,而不是函数,包含在assert.h 头文件中。如果其后面括号里的值为假,
则程序终止运行,并提示出错;如果后面括号里的值为真,则继续运行后面的代码。这个
宏只在Debug 版本上起作用,而在Release 版本被编译器完全优化掉,这样就不会影响代码
的性能。 - assert 宏只是帮助我们调试代码用的,它的一切作用就是让我们尽可能的在调试函数的时候
把错误排除掉。参数出现错误并非本函数有问题,而是调用者传过来的实参有问题。assert 宏可以帮助我们定位错误,而不是排除错误。 - 为指针分配内存太小——导致越界行为。
例:在字符串拷贝时,忘记结束标志“/0”导致拷贝后的数据出错,解决办法是是加上这个字符串结束标志符:char *p2 = (char *)malloc(sizeof(char)*strlen(p1)+1*sizeof(char)); - 内存泄漏
会产生泄漏的内存就是堆上的内存(这里不讨论资源或句柄等泄漏情况),也就是说由
malloc 系列函数或new 操作符分配的内存。如果用完之后没有及时free 或delete,这块内存
就无法释放,直到整个程序终止。 - malloc 函数的原型:(void *)malloc(int size)。
- 使用malloc 函数需要几个要求:
①内存分配给谁?
②分配多大内存?
③是否还有足够内存分配?
④内存的将用来存储什么格式的数据,即内存用来做什么?
⑤分配好的内存在哪里? - 使用malloc后,记得用if(NULL!=p)检验是否分配成功。
- malloc 函数可以申请0 字节内存,函数并不返回NULL,而是返回一个正常的内存地址。但是你却无法使用这块大小为0 的内存,并且if(NULL != p)语句校验将不起作用。
- 内存释放
既然有分配,那就必须有释放。
要注意对应关系。 - 使用free 函数之后指针变量p 本身保存的地址并没有改变,需要重新把p
的值变为NULL。