第20章 指针 二 为指针分配和释放空间.docx

上传人:b****7 文档编号:11096885 上传时间:2023-02-25 格式:DOCX 页数:35 大小:60.30KB
下载 相关 举报
第20章 指针 二 为指针分配和释放空间.docx_第1页
第1页 / 共35页
第20章 指针 二 为指针分配和释放空间.docx_第2页
第2页 / 共35页
第20章 指针 二 为指针分配和释放空间.docx_第3页
第3页 / 共35页
第20章 指针 二 为指针分配和释放空间.docx_第4页
第4页 / 共35页
第20章 指针 二 为指针分配和释放空间.docx_第5页
第5页 / 共35页
点击查看更多>>
下载资源
资源描述

第20章 指针 二 为指针分配和释放空间.docx

《第20章 指针 二 为指针分配和释放空间.docx》由会员分享,可在线阅读,更多相关《第20章 指针 二 为指针分配和释放空间.docx(35页珍藏版)》请在冰豆网上搜索。

第20章 指针 二 为指针分配和释放空间.docx

第20章指针二为指针分配和释放空间

第二十章指针二为指针分配和释放空间

 

20.1理解指针的两种“改变”

 20.1.1改变指针的值

 20.1.2改变指针所指的变量的值

 20.1.3两种改变?

一种改变?

20.2C++方式的内存分配与释放new和delete

 20.2.1new

 20.2.2在new时初始化内存的值

 20.2.3delete

 20.2.4实验:

new和delete

 20.2.5new和delete的关系

20.3new[]和delete[]

 20.3.1new[]/delete[]基本用法

 20.3.2new[]/delete[]示例

 20.3.3详解指向连续空间的指针

20.4delete/delete[]的两个注意点

 20.4.1一个指针被删除时,应指向最初的地址

 20.4.2已释放的空间,不可重复释放

20.5C方式的内存管理

 20.5.1分配内存malloc函数

 20.5.2释放内存free函数

 20.5.3重调空间的大小:

realloc函数

 20.5.4malloc、realloc、free的例子

 

20.1理解指针的两种“改变”

普通变量(非指针,简单类型变量)只能改变值:

 

1)inta=100;

2)...

3)a=200;

 

第1行代码,声明int类型变量a,并且初始化a的值为100。

到第3行代码,变量a的值被改变成200。

 

对于非指针的简单变量,能被程序改变的,只有这一种。

而指针变量,似乎有两种改变。

 

20.1.1改变指针的值

这一点和普通变量一致。

但要特别注意,“改变指针的值”引起的结果是什么?

其实就是“改变指针的指向”。

因为,指针的值是某个变量的地址。

假如指针P原来的值是A变量的地址,现在改为B变量的地址。

我们称为:

“P由指向A改为指向B”。

这就是指针的第一种改变。

以下是示例代码:

int*P;

intA,B;

 

1)P=&A;

2)...

3)P=&B;

 

1)行代码中,P的值为&A,即P指向变量A。

到3)行代码中,P的值变为&B,即改为指向变量B。

 

下面讲:

指针的第二种改变。

通过指针,改变指针所指的变量的值。

 

20.1.2改变指针所指的变量的值

 

复习前一章,我们知道通过*(地址解析符),可以得到、或改变指针所指的变量的值。

 

int*P;

intA=100;

 

P=&A;

*P=200;

 

cout<

 

代码中加粗的那一行:

*P=200;,其作用完全等同于:

A=200;

所以,最后一行输出的结果是200。

 

这就是指针的第二种改变:

所指变量的值,被改变了。

 

20.1.3两种改变?

一种改变?

 

两种改变的意义不同:

改变一:

改变指针本身的值(改变指向)。

改变二:

改变指针指向的变量的值。

 

从代码上看:

第一种改变,P=&A;左值(等号左边的值)是变量本身,右值则是一个地址。

而第二种改变,*P=200;左值通过星号对P操作,来取得P指向的变量;右值是普通的值。

 

理解,区分对指针的两种改变,才能学会如何使用指针。

 

请思考:

上一章讲的“指针的加减操作”,是对指针的哪一种改变?

 

最后需要说明,严格意义上,指针仍然只有一种改变,即改变指针本身的值。

改变指针指向的变量,应视为对另一变量的改变,只不过在代码上,它通过指针来进行,而不是直接对另一变量进行操作。

 

为指针分配、释放内存空间

 

之前,我们给指针下的定义是“指针是一个变量,它存放的值是另一个变量的地址”。

比如:

inta;

int*p=&a;

 

看,a就是“另一个变量”,p指向了a。

我们知道,变量总是要占用一定的内存空间,比如上面的a,就占用了4个字节(sizeof(int))。

这四个字节属于谁?

当然属于变量a,而不是p。

 

现在要讲的是:

也可以单独为指针分配一段新的内存空间。

这一段内容不属于某个变量。

 

20.2C++方式的内存分配与释放new和delete

在内存管理上,C++和C有着完全不同的两套方案。

当然,C++的总是同时兼容C。

C的那一套方案在C++里同样可行。

我们首先看看纯C++的那一套:

new和delete。

new,从字面上看意思为“新”;而delete字面意思为“删除”。

二者在C++中内存管理中大致的功能,应是一个为“新建”,一个为“删除”。

 

20.2.1new

 

new是c++的一个关键字。

被当作像+、-、*、/一样的操作符。

它的操作结果是在申请到一段指定数据类型大小的内存。

 

语法:

 

指针变量=new数据类型;

 

new将做三件事:

 

1、主动计算指定数据类型需要的内存空间大小;

2、返回正确的指针类型;

3、在分配内存的一,将按照语法规则,初始化所分配的内存。

 

这是什么意思呢?

看看例子吧:

 

int*p;

p=newint;

 

和以往不一样,p这回不再“寄人篱下”,并不是指向某个已存在的变量,而是直接指向一段由new分配而来的新内存空间。

 

“p指向一段由new分配而来的新内存空间”这句话等同于:

“new分配一段新的内存空间,然后将该内存空间的地址存入到变量p中。

所以,最终p中仍然是存储了一个变量的地址,只是,这是一个“无名”变量。

 

指向原有的某个变量,和指向一段新分配的内存空间,有什么区别呢?

“原有的变量”,可以比喻成指向一间原有的,并且有主的房间。

而“新分配的内存空间”,则像是一个“临时建筑物”。

我们必须在不用它的时候,主动将它拆迁。

拆迁的工作由delete来完成。

 

当指针变量通过new,而得到一个内存地址后,我们就可以像以前的所说的,通过该指针,通过*号,而对该内存地址(一个无名的变量),进行操作。

如:

int*p=newint;

*p=100;

cout<<*p<

 

屏幕将输出100。

 

20.2.2在new时初始化内存的值

 

new也可以在申请内存空间时,直接设置该段内存里要放点什么.

 

语法:

 

指针变量=new数据类型(初值);

 

这样,上例可以改为:

 

int*p=newint(100);

cout<<*p<

 

如果你申请的是字符类型的空间,并且想初始化为‘A':

 

char*pchar=newchar('A');

 

20.2.3delete

 

语法:

delete指针变量;

 

delete将释放指定指针所指向的内存空间。

 

举例:

 

int*p;

p=newint;

 

*p=100;

cout<<*p<

 

deletep;

 

system("PAUSE");

 

注意,当一个指针接受delete操作后,它就又成了一个“指向不明”的指针。

尽管我们可以猜测它还是指向“原来的房子”,然而,事实上,那座“房子”已经被delete“拆迁”掉了。

 

20.2.4实验:

new和delete

 

很简单的例子。

第一步:

首先,在CB新建一个控制台程序。

然后把上一小节的代码放到main()函数内。

运行。

结果如下:

 

(new和delete)

 

按任意键退出后,保存工程(Ctrl+Shift+S)。

 

第二步:

接下来我们来观察指针变量被delete之后,所指向的内存会是什么。

但,这是一件犯了C、C++编程大忌的事:

访问一个已经delete的指针的值。

如果你最近运气很差,你的CB可能会被强行退出。

所以,你明白我们为什么要先存盘了,对不?

 

在前面的代码中,加入以下加粗加红的一行(同时,你也应注意我的加的注释):

 

int*p;

p=newint;

 

*p=100;

cout<<*p<

 

deletep;   //p所指向的内存空间已经被释放

 

cout<<*p<

 

system("PAUSE");

 

运行结果:

 

(访问delete之后的指针)

 

44244844?

在你的机器可能不是这个数,但一定同样是怪怪的值。

原来是好端端的100,现在却成了44244844。

不要问我这是为什么?

昨天来时,美眉还住在这里一座别致小阁楼里,今日故地重游,这里竟成废墟一片,依稀只见破墙上尚有:

“拆!

——城建局”的字样?

 

new是管建房的,而delete就一个字:

拆!

 

请大家自行在CB上完成本实验。

我没有提供本题的实际工程。

20.2.5new和delete的关系

 

如果只有“建房”而没有“拆房”,那么程序就会占用内存越来越多。

所以,当使用new为某个指针分配出内存空间后,一定要记得在不需要再使用时,用delete删除。

下面是一个例子。

演示new和delete的对应使用。

 

//建屋和入住:

1)int*p=newint(100);

 

//使用:

2)cout<<*p<

 

//拆:

3)deletep;

 

看,第1句,申请了4字节的内存空间,同时存入值为100的整数。

第2句,在屏幕上输出入住者的值(100)。

第3句,释放内存(这4字节被系统收回准备做其它用途)。

入住者呢?

自然消失了。

 

前面举的例子都是在new一个int类型,其它类型也一样:

 

char*a=newchar('A');

cout<<*a<

*a='B';

cout<<*a<

deletea;

 

bool*b=newbool;

*b=true;

 

if(*b)

  cout<<"true"<

else

 cout<<"fale"<

 

但是这些都是简单数据类型,如果要分配数组一样的连续空间,则需要使另一对武器。

 

20.3new[]和delete[]

 

new/delete用于分配和释放单个变量的空间,而new[]/delete[]则用于分配连续多个变量的存间。

 

20.3.1new[]/delete[]基本用法

 

new[]语法:

 

指针变量=new数据类型[元素个数]

 

语法实例:

 

int*p=newint[20];

 

首先,你需要迅速回想一下,如果是int*p=newint(20);那么该是什么作用?

否则你很容易在事后把二者混了。

 

实例中,用new申请分配了20个连续的整数所需的空间,即:

20*sizeof(int)=80个字节。

图示为:

(指针变量p指向一段连续的内存空间)

 

newint只是分配了一个整数的内存空间,而newint[N]却分配了N个整数的连续空间。

看来,new[]比new“威力更猛”,所以,我们同样得记得:

用new[]分配出空间,当不在需要时,必须及时调用delete[]来释放。

 

delete[]语法:

 

delete[]指针变量;

 

如:

 

//分配了可以存放1000个int的连续内存空间:

int*p=newint[1000]; 

 

//然后使用这些空间:

……

 

//最后不需要了,及时释放:

delete[]p;

 

20.3.2new[]/delete[]示例

 

在WindowsXP、WindowsNT或Windows2000中,按Ctrl+Alt+Del(其它操作系统,如Windows98/Me等千万不要按些组合键,否则电脑将重启)。

可以调出Windows任务管理器,其中要以看出当前粗略的的内存使用量。

下面我们结合该工具,写一个程序,先分配100M的内存,再释放。

 

这是程序代码的抓图:

 

各步运行结果:

程序显示

任务管理器抓图

第一步:

分配内存之前

(任务管理显示我的机器使用了207兆的内存)

第二步:

分配了100兆的内存

(多出了100M)

第三步:

又释放出这100兆

(回到207兆)

 

注意:

使用new得来的空间,必须用delete来释放;使用new[]得来的空间,必须用delete[]来释放。

彼此之间不能混用。

 

用new[]分配出连续空间后,指针变量“指向”该空间的首地址。

 

20.3.3详解指向连续空间的指针

 

在通过new[]指向连续空间以后,p就变得和一个一维数组很是类似。

我们先来复习一下数组相关知识。

 

假设是这么一个数组:

 

intarr[20];

 

则arr的内存示意图为(为了不太占用版面我缩小了一点):

 

(数组arr的内存示意)

 

和指针变量相比,数组没有一个单独的内存空间而存放其内存地址。

即:

指针变量p是一个独立的变量,只不过它的值指向另一段连续的内存空间;而数组arr,本身代表的就是一段连续空间。

 

如果拿房间来比喻。

指针和数组都是存放地址。

只不过,指针是你口袋里的那本通讯录上写着的地址,你可以随时改变它的内容,甚至擦除。

而数组是你家门楣上钉着的地址,你家原来是“复兴路甲108号”,你绝对不能趁月黑天高,把它涂改为“唐宁街10号”。

 

数组是“实”的地址,不能改变。

当你和定义一个数组,则这个数组就得根据它在内存中的位置,得到一个地址,如上图中的“0x1A000000”。

只要这个数组存在,那么它终生的地址就是这个值。

 

指针是一个“虚”的地址,可以改变地址的值。

当你定义一个指针变量,这个变量占用4个字节的内存,你可以往这4字节的内存写入任意一个值,该值被当成一个内存地址。

比如,你可以写入上面的“0x1A000000”,此时,指针p指向第一个元素。

也可以改为“0x1A000003”,此时,指针p指向第二个元素。

 

所以,当p通过new[]指向一段连续空间的结果是,p是一个指向数组的指针,而*p是它所指的数组。

 

我们来看实例,首先看二者的类似之处。

下面左边代码使用数组,右边代码使用指针。

 

数组

指针(通过new[]所得)

//定义:

intarr[20];

//定义:

int*p=newint[20];

 

//让第一个元素值为100:

arr[0]=100;

 

//让第一个元素值为100:

p[0]=100;

//让后面19个元素值分别为其前一元素加50:

for(inti=1;i<20;i++)

{

  arr[i]=arr[i-1]+50;

}

//让后面19个元素值分别为其前一元素加50:

for(inti=1;i<20;i++)

{

  p[i]=p[i-1]+50;

}

//输出所有元素:

for(inti=0;i<20;i++)

{

 cout<

}

//输出所有元素:

for(inti=0;i<20;i++)

{

 cout<

}

//也可以不用[],而通过+号来得到指定元素:

//当然,对于数组,更常用的还是[]操作符。

cout<<*(arr+0)<

cout<<*(arr+1)<

cout<<*(arr+1)<

 

输出结果:

 100

 150

 200

//也可以不用[],而通过+号来得到指定元素:

//其实,对于指针,这样的+及-操作用得还要多点。

cout<<*(p+0)<

cout<<*(p+1)<

cout<<*(p+1)<

 

输出结果:

 100

 150

 200

 

 

当指针变量P通过new[]指向一连续的内存空间:

1、p[N]得到第N个元素(0<=N<元素个数);2、*(p+N)同样得到第N个元素(0<=N<元素个数)

如p[0]或*(p+0)得到内存空间第0个元素;

 

把上面右边代码中的大部分p替换为arr,则和左边代码变得一模一样。

 

下面再来比较二者的不同。

 

数组

指针

//定义并且初始化:

intarr[20]={0,1,2,3,4,5,6,7,8,9,0,……,19};

 

//定义、并且生成空间,但不能直接初始空间的内容:

int*p=newint[20]{0,1,2,3,4……}//错!

 

//只得通过循环一个个设置:

for(inti=0; i<20;i++)

{

  p[i]=i;

}

//不能通过对数组本身+或-来改变数组的位置:

arr=arr+1; //错!

cout<<*arr<

 

arr++; //错!

cout<<*arr<

 

arr--; //错!

cout<<*arr<

 

输出结果:

无,因为程序有语法错误,通不过编译。

//可以通过+或-操作直接改变指针:

p=p+1;

cout<<*p<

 

p++;

cout<<*p<

 

p--;

cout<<*p<

 

输出结果:

 1

 2

 1

//释放空间:

//数组所带的空间由系统自动分配及回收

//无须也无法由程序来直接释放。

//释放空间:

//指向连续空间的指针,必须使用delete[]来释放

delete[]p;

 

关于指针本身的+和-操作,请复习上一章相关内容。

 

接下来的问题也很重要。

 

20.4delete/delete[]的两个注意点

指针通过new或new[],向系统“申请”得到一段内存空间,我们说过,这段内存空间必须在不需要将它释放了。

有点像人类社会的终极目标“共产主义”下的“按需分配”。

需要了就申请,不需要了,则主动归还。

 

现在问题就在于这个“主动归还”。

当然,指针并不存在什么思想觉悟方面的问题,说光想申请不想归还。

真正的问题是,指针在某些方面的表现似乎有些像“花心大萝卜”。

请看下面代码,演示令人心酸的一幕。

 

/*

 初始化p -----p的新婚

 通过new,将一段新建的内存“嫁给”指针p

 这一段分配的内存,就是p的原配夫妻

*/

int*p=newint[100]; 

 

 

/*

  使用p -----恩爱相处

  N多年恩爱相处,此处略去不表

*/

……

 

/*

  p改变指向----分手

*/

 

intgirl[100];  //第三者出现

p=girl;        //p就这样指向girl

 

 

/*

  delete[]p---- 落幕前的灾难 

 

  终于有一天,p老了,上帝选择在这一时刻

  惩罚他

*/

 

delete[]p;

 

扣除注释,上面只有4行代码。

这4行代码完全符合程序世界的宪法:

语法。

也就是说对它们进行编译,编译器会认为它们毫无错误,轻松放行。

 

但在灾难在delete[]p时发生。

我们原意是要释放p最初通过newint[100]而得到的内存空间,但事实上,p那时已经指向girl[100]了。

结果,第一、最初的空间并没有被释放。

第二、girl[100]本由系统自行释放,现在我们却要强行释放它。

 

20.4.1一个指针被删除时,应指向最初的地址

 

当一个指针通过+,-等操作而改变了指向;那么在释放之前,应确保其回到原来的指向。

 

比如:

 

int*p=newint[3];

 

*p=1;

cout<<*p<

 

p++;   //p的指向改变了,指向了下一元素

*p=2;

cout<<*p<

 

//错误的释放:

delete[]p;

 

在delete[]p时,p指向的是第二个元素,结果该释放将产生错位:

第一个元素没有被释放,而在最后多删除了一个元素。

相当你盖房时盖的是前3间,可以在拆房时,漏了头一间,从第二间开始拆起,结果把不是你盖的第4房间倒给一并拆了。

 

如何消除这一严重错误呢?

第一种方法是把指针正确地"倒"回原始位置:

 

p--;

delete[]p;

 

但当我们的指针指向变化很多次时,在释放前要保证一步不错地一一退回,会比较困难。

所以另一方法是在最初时“备份”一份。

在释放时,直接释放该指针即可。

 

int*p=newint[3];

int*pbak=*p;   //备份

 

//移动p

……

 

//释放:

delete[]pbak;

 

由于pbak正是指向p最初分配后的地址,我们删除pbak,就是删除p最初的指向。

此时我们不能再删除一次p。

这也就引出new/delete及new[]/delete[]在本章的最后一个问题。

 

20.4.2已释放的空

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > PPT模板 > 可爱清新

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1