C高级(4)--内存管理

1.内存分配方式有三种:

1) 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的

整个运行期间都存在。例如全局变量,static 变量。

2) 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函

数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集

中,效率很高,但是分配的内存容量有限。

3) 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc new 申请任意

多少的内存,程序员自己负责在何时用free delete 释放内存。动态内存的生存

期由我们决定,使用非常灵活,但问题也最多。

2.常见的内存错误及其对策

内存错误:编译器不能发现该错误,通常在程序运行时才能捕捉到。

常见的内存错误及其对策如下:

� 内存分配未成功,却使用了它。没有意识到内存分配会不成功。常用解决办法是,在使用内存之前检查指针是否为NULL。一般使用断言判断即可。

� 内存分配虽然成功,但是尚未初始化就引用它。犯这种错误主要有两个起因:一是没有初始化的观念;二是误以为内存的缺省初值全为零,导致引用初值错误(例如数组)。

� 内存分配成功并且已经初始化,但操作越过了内存的边界。如数组下标越界。

� 忘记了释放内存,造成内存泄露。动态内存的申请与释放必须配对,程序中malloc 与free 的使用次数一定要相同,否则肯定有错误(new/delete 同理)。

� 释放了内存却继续使用它。有三种情况:

(1)程序中的对象调用关系过于复杂,实在难以搞清楚某个对象究竟是否已经释放了内存,此时应该重新设计数据结构,从根本上解决对象管理的混乱局面。

(2)函数的return 语句写错了,注意不要返回指向“栈内存”的“指针”或者“引用”,因为该内存在函数体结束时被自动销毁。

(3)使用free 或delete 释放了内存后,没有将指针设置为NULL。导致产生“野指针”。

3.指针与数组的对比

数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。数组名对应着

(而不是指向)一块内存,其地址与容量在生命期内保持不变,只有数组的内容可以改

变。

指针可以随时指向任意类型的内存块,它的特征是“可变”,所以我们常用指针来操作动态内存。指针远比数组灵活,但也更危险。

1.修改内容

数组字符串创建是在栈上。指针指向的字符串处于静态存储区,不可以修改。char a[] = “hello”;   a[0] = ‘X’;   cout << a << endl;    //可行

char *p = “world”;   p[0] = ‘X’;   cout << p << endl;   //不可行

2.内容复制与比较

不能对数组名进行直接复制与比较。复制使用strcpy,比较使用strcmp。

指针复制和比较:先申请内存容量为strlen(a)+1个字符的内存,再用strcpy  进行字符串复制。同理,比较使用strcmp。

3.计算内存容量

Sizeof("abcdef") == strlen("abcdef")+1 == 7

Char *p = "abcdef" sizeof(p) == sizeof(char *) == 地址所占大小== 4  :得到的是地址大小。而不是指针所指向的内存容量。

4.指针参数是如何传递内存的?

如果函数的参数是一个指针,不要指望用该指针去申请动态内存。编译器总是要为函数的每个参数制作临时副本,指针参数p 的副本是 _p,编译器使 _p = p。如果函数体内的程序修改了_p 的内容,就导致参数的内容作相应的修改。这就是指针可以用作输出参数的原因。在本例中,_p 申请了新的内存,只是把_p 所指的内存地址改变了,但是丝毫未变。所以函数GetMemory并不能输出任何东西。事实上,每执行一次GetMemory 就会泄露一块内存,因为没有用free 释放内存。如果非得要用指针参数去申请内存,那么应该改用“指向指针的指针”。

//EX1:

void GetMemory2(char **p, int num)

{

*p = (char *)malloc(sizeof(char) * num);

}

void Test2(void)

{

char *str = NULL;

GetMemory2(&str, 100); // 注意参数是 &str,而不是str

strcpy(str, "hello");

Cout<< str << endl;

free(str);

}

由于“指向指针的指针”这个概念不容易理解,我们可以用函数返回值来传递动态

内存。这种方法更加简单://EX2:

char *GetMemory3(int num)

{

char *p = (char *)malloc(sizeof(char) * num);

return p;

}

void Test3(void)

{

char *str = NULL;

str = GetMemory3(100);

strcpy(str, "hello");

cout<< str << endl;

free(str);

}

用函数返回值来传递动态内存这种方法虽然好用,但是常常有人把return 语句用错

了。这里强调不要用return 语句返回指向“栈内存”的指针,因为该内存在函数结束时

自动消亡://EX3:

char *GetString(void)

{

char p[] = "hello world";

return p; // 编译器将提出警告

}

void Test4(void)

{

char *str = NULL;

str = GetString(); // str不再是NULL指针  但str的内容是垃圾 ,因为p在函数结束后出栈。

cout<< str << endl;

}

若是改成使用指针指向字符串,即如下//EX4:

char *GetString2(void)

{

char *p = "hello world"; //是常量字符串,位于静态存储区,它在程序生命期内恒定不变.它返回的始终是同一个“只读”的内存块。

return p;

}

void Test5(void)

{

char *str = NULL;

str = GetString2();

cout<< str << endl;

}

5.free和delete把指针怎么啦?

Free和delete只是把指针所指向的内存释放掉,但是并没有把指针本身干掉。即:free(p)把p指向的内容释放掉,但是p的地址还是原来申请时的地址,在free(p)之后不能使用if(NULL != p)为真再次使用p,此时的p(地址)指向内存内容是垃圾。此时p 成了“野指针”。这时应该p=NULL即可。

6.动态内存会自动释放吗?

函数体内的局部变量在函数结束时自动消亡。但是局部指针变量所指向的内存(堆空间)不会自动释放。注意如下2点:

1)指针消亡了,不表示所指向的内存会自动释放。

2)内存被释放了,不表示指针消亡或成了NULL。

Free(p):释放p指针所指向的内存空间。在free(p)之后在p=NULL之前,p地址存在,要是引用就成为了野指针。P是栈变量,当函数结束时,p的内容弹出栈。

7.杜绝野指针

野指针不是NULL指针,是指向垃圾内存的指针。野指针是很危险的,if(NULL != p)对野指针不起作用。

野指针成因有3种:

A.指针变量没有初始化。即在声明时,编译器不会默认置指针为NULL。此时的指针地址是随机的。所以在指针声明时,要么置NULL,要么指向合法内存。

B.指针p被free或者delete之后,没有置为NULL,误以为p是合法的,再次使用p。

C.指针操作超越了变量的作用范围。EX:

A *p;

{

A a;

P = &a; //注意a的生命周期是在花括号之内

}

P->func(); //p是野指针

8.有了malloc/free为什么还要new/delete?

Malloc/free是C/C++标准库函数。而new/delete是C++的运算符。

对于对象而言,malloc/free满足不了动态对象的需求。因此 C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算符delete。注意new/delete 不是库函数。

New/delete除了实现了malloc/free的动态内存分配功能外,还实现了对象的构造和析构过程。

既然new/delete的功能完全覆盖了malloc/free,为什么C++不把malloc/free淘汰掉?这是因为C++程序经常要调用C,C要调用malloc/free。

9.内存耗尽怎么办?

内存耗尽,malloc和new申请内存失败,返回NULL指针。有3种方式处理内存耗尽的方法:

1.判断指针是否为NULL,若是立即return,终止本函数。

2.判断指针是否为NULL,若是立即exit(1),终止整个程序。

3.为new和malloc设置异常处理函数。VC++使用_set_new_hander函数用户自定义处理异常。

内存耗尽可能性不大,因为32 位操作系统支持“虚存”,内存用完了,自动用硬盘空间顶替。

10.malloc/free的使用要点

Malloc函数原型:void* malloc(size_t size);应该注意2个方面:

1.返回类型是void*,注意类型转换。

2.Malloc申请内存本身不识别申请内存是什么数据类型,只是关心内存的总字节数,所以使用sizeof(type)提供了方便。

Free函数的原型:void free(void* memblock);

1.释放指针函数为何如此简单?因为在申请时内存容量已经知道了。

2.如果p是NULL,可以free任何多次都不会出问题,如果p不是NULL,那么free(p)两次就会导致程序运行错误。

11.new/delete的使用要点

New使用比malloc函数简单很多,是因为new内置了sizeof、类型转换和类型安全检查。new在创建动态对象时同时完成了初始化工作。

new在创建对象数组,只能使用无参构造函数。在使用delete释放对象数组时,不要丢了符号[].

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值