林锐C语言--高质量C/C++编程(第七章内存管理)

林锐C语言–高质量C/C++编程(第七章内存管理)

林锐C语言--高质量C/C++编程(第七章内存管理)

  • 林锐C语言--高质量C/C++编程(第七章内存管理)
    • 7.1 内存的分配方式
    • 7.3 指针与数组的对比
    • 7.4 指针参数是如何传递内存的?
      • 1)用指向指针的指针
      • 2)用函数返回值
      • 3)返回常量字符串
      • 4)栈指针
    • 7.5 free 和 delete 把指针怎么啦
    • 7.6 切记要初始化指针,释放指针后要置成空指针。
    • 7.9 内存耗尽怎么办?
    • 7.10 malloc/free 的使用要点
    • 7.12 一些心得体会

7.1 内存的分配方式

内存分配方式有三种:

  1. 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static 变量。
  2. 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
  3. 从堆上分配,亦称动态内存分配。程序在运行的时候用 malloc 或 new 申请任意多少的内存,程序员自己负责在何时用 free 或 delete 释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。

要对堆区开辟的内存判断是否为NULL
开辟的内存要进行初始化
注意初始化成功时不要越界
动态内存的申请与释放必须配对,程序中 malloc与free的使用次数一定要相同
注意不要返回指向“栈内存”的“指针”
将指针设置为 NULL

7.3 指针与数组的对比

数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。数组名对应着(而不是指向)一块内存,其地址与容量在生命期内保持不变,只有数组的内容可以改变。
指针可以随时指向任意类型的内存块,它的特征是“可变”,所以我们常用指针来高质量 C++/C 编程指南,操作动态内存。指针远比数组灵活,但也更危险。
指针 p 指向常量字符串“world”(位于静态存储区,内容为 world\0),
常量字符串的内容是不可以被修改的。从语法上看,编译器并不觉得语句 p[0]= ‘X’有什么不妥,但是该语句企图修改常量字符串的内容而导致运行错误。
数组之间比较用 strcmp ,赋值用strcpy。
sizeof(字符串) ,’\0’也算字符串大小。

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

1)用指向指针的指针

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 );
}

2)用函数返回值

用函数返回值来传递动态内存。这种方法更加简单。

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 );
}

3)返回常量字符串

函数 Test5 运行虽然不会出错,但是函数 GetString2 的设计概念却是错误的。 因为 GetString2 内的“hello world”是常量字符串,位于静态存储区,它在程序生命期内恒定不变。无论什么时候调用 GetString2,它返回的始终是同一个“只读”的内存块。

char *GetString2( void )
{
 char * p =" hello world ";
 return p ;
}
 void Test5( void )
{
 char * str = NULL ;
 str =GetString2();
 cout << str << endl ;
}

4)栈指针

用函数返回值来传递动态内存这种方法虽然好用,但是常常有人把 return 语句用错了。这里强调不要用 return 语句返回指向"栈内存"的指针,因为该内存在函数结束时自动消亡,见示例7-4-4。

char * GetString ( void )char p []=" hello world ";
 return p ;//编译器将提出警告void Test4( void )char * str = NULL ;
 str = GetString ();// str 的内容是垃圾
 cout << str << endl ;

7.5 free 和 delete 把指针怎么啦

别看 free 和 delete 的名字恶狠狠的(尤其是 delete),它们只是把指针所指的内存给释放掉,但并没有把指针本身干掉

char *p = (char *) malloc(100); 
strcpy(p,"hello");
free(p);
...
if(p != NULL) // 没有起到防错作用
//p所指的内存被释放,但是 p所指的地址仍然不变strcpy(p, "world");// 出错

7.6 切记要初始化指针,释放指针后要置成空指针。

要么将指针设置为 NULL,要么让它指向合法的内存。不要越界访问。例如

 char *p = NULL; *
 char *str = (char *) malloc(100); 

7.9 内存耗尽怎么办?

如果在申请动态内存时找不到足够大的内存块,malloc 和 new 将返回 NULL 指针,宣告内存申请失败。通常有三种方式处理“内存耗尽”问题。
(1)判断指针是否为 NULL,如果是则马上用 return 语句终止本函数。例如:

void Func(void) 
{ 
	A *a = new A; 
	if(a == NULL) 
	{ 
		 return; 
	 } 
//} 

(2)判断指针是否为 NULL,如果是则马上用 exit(1)终止整个程序的运行。例如:

void Func(void) 
{ 
	A *a = new A; *
	if(a == NULL) 
	{ 
		 cout << “Memory Exhausted” << endl; 
		 exit(1); 
	} 
// … 
}

(3)为 new 和 malloc 设置异常处理函数。例如 Visual C++可以用_set_new_hander 函数为 new 设置用户自己定义的异常处理函数,也可以让 malloc 享用与 new 相同的异常处理函数。详细内容请参考 C++使用手册
上述(1)(2)方式使用最普遍。如果一个函数内有多处需要申请动态内存,那么方式(1)就显得力不从心(释放内存很麻烦),应该用方式(2)来处理。很多人不忍心用 exit(1),问:“不编写出错处理程序,让操作系统自己解决行不行?”
不行。如果发生“内存耗尽”这样的事情,一般说来应用程序已经无药可救。
如果不用 exit(1) 把坏程序杀死,它可能会害死操作系统。道理如同:如果不把歹徒击毙,歹徒在老死之前会犯下更多的罪.

有一个很重要的现象要告诉大家。对于32位以上的应用程序而言,无论怎样使用malloc与new,几乎不可能导致“内存耗尽”。
我在 Windows 98 下用 Visual C++编写了测试程序,见示例 7-9。这个程序会无休止地运行下去,根本不会终止。因为 32 位操作系统支持“虚存”,内存用完了,自动用硬盘空间顶替。我只听到硬盘嘎吱嘎吱地响,Window 98 已经累得对键盘、鼠标毫无反应。

---//试图耗尽操作系统的内存
void main(void) 
{ 
 	float *p = NULL; 
	 while(TRUE) 
	 { 
		 p = new float[1000000]; 
		 cout << “eat memory” << endl; 
		 if(p==NULL) 
		     exit(1); 
 	} 
}

7.10 malloc/free 的使用要点

函数 malloc 的原型如下: void * malloc(size_t size); 用 malloc 申请一块长度为 length 的整数类型的内存,程序如下:

 int *p = (int *) malloc(sizeof(int) * length);

我们应当把注意力集中在两个要素上:“类型转换"和"sizeof”。

  • malloc 返回值的类型是 void *,所以在调用malloc 时要显式地进行类型转换,将void * 转换成所需要的指针类型。
  • malloc函数本身并不识别要申请的内存是什么类型,它只关心内存的总字节数。我们通常记不住 int, float 等数据类型的变量的确切字节数。例如int变量在16位系统下是2个字节,在32位下是4个字节;而float变量在16位系统下是4个字节,在32位下也是4个字节。最好用以下程序作一次测试:
cout<<sizeof (char)<<endl;
cout<<sizeof(int)<<endl;
cout << sizeof (unsigned int) << endl;
cout << sizeof(long)<<endl;
cout << sizeof (unsigned long)<< endl;
cout<<sizeof(float)<<endl;
cout << sizeof (double)<<endl;
cout<< sizeof (void *)<<endl;

在malloc的"()"中使用sizeof运算符是良好的风格,但要当心有时我们会昏了头,写出 p = malloc(sizeof §)这样的程序来。

  • 函数free 的原型如下:
void free( void * memblock );

为什么 free 函数不象 malloc 函数那样复杂呢?这是因为指针p的类型以及它的内存的容量事先都是知道的,语句free(p)能正确地释放内存。如果 p 是 NULL 那么 free 对 p 无论操作多少次都不会出问题。如果 p 不是 NULL指针,那么 free连续操作两次就会导致程序运行错误。

7.12 一些心得体会

(1)越是怕指针,就越要使用指针。不会正确使用指针,肯定算不上是合格的程序员。
(2)必须养成“使用调试器逐步跟踪程序”的习惯,只有这样才能发现问题的本质

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

进击的横打

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值