第二十章 指针 二 为指针分配和释放空间http://17de.com/library/CPP/ls20.htm#20.5.2

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

20.1.1 改变指针的值

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

20.1.3 两种改变?一种改变?

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

20.2.1 new

20.2.2 在new 时初始化内存的值

20.2.3 delete

20.2.4 实验: new 和 delete

20.2.5 new 和 delete 的关系

20.3 new [] 和 delete []

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

20.3.2 new []/ delete[] 示例

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

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

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

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

20.5 C 方式的内存管理

20.5.1 分配内存 malloc 函数

20.5.2 释放内存 free 函数

20.5.3 重调空间的大小: realloc 函数

20.5.4 malloc、realloc、free的例子

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

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

1) int a = 100;

2) ...

3) a = 200;

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

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

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

20.1.1 改变指针的值

这一点和普通变量一致。但要特别注意,“改变指针的值”引起的结果是什么?其实就是“改变指针的指向”。

因为,指针的值是某个变量的地址。假如指针P原来的值是A变量的地址,现在改为B变量的地址。我们称为:“P由指向A改为指向B”。这就是指针的第一种改变。

以下是示例代码:

int* P;

int A,B;

1) P = &A;

2) ...

3) P = &B;

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

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

下面讲:指针的第二种改变。通过指针,改变指针所指的变量的值。

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

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

int* P;

int A = 100;

P = &A;

*P = 200;

cout << A << endl;

代码中加粗的那一行:*P = 200; ,其作用完全等同于:A = 200;

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

这就是指针的第二种改变:所指变量的值,被改变了。

20.1.3 两种改变?一种改变?

两种改变的意义不同:

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

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

从代码上看:

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

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

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

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

最后需要说明,严格意义上,指针仍然只有一种改变,即改变指针本身的值。改变指针指向的变量,应视为对另一变量的改变,只不过在代码上,它通过指针来进行,而不是直接对另一变量进行操作。

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

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

比如:

int a;

int* p = &a;

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

我们知道,变量总是要占用一定的内存空间,比如上面的a,就占用了4个字节(sizeof(int))。这四个字节属于谁?当然属于变量a,而不是p。

现在要讲的是:也可以单独为指针分配一段新的内存空间。这一段内容不属于某个变量。

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

在内存管理上,C++ 和 C 有着完全不同的两套方案。当然,C++的总是同时兼容C。C的那一套方案在C++里同样可行。

我们首先看看纯C++的那一套: new 和 delete。

new ,从字面上看意思为 “新”;而delete 字面意思为“删除”。二者在C++中内存管理中大致的功能,应是一个为“新建”,一个为“删除”。

20.2.1 new

new 是 c++ 的一个关键字。被当作像 +、-、* 、/ 一样的操作符。它的操作结果是在申请到一段指定数据类型大小的内存。

语法:

指针变量 = new 数据类型;

new 将做三件事:

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

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

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

这是什么意思呢?看看例子吧:

int* p;

p = new int;

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

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

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

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

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

“原有的变量”,可以比喻成指向一间原有的,并且有主的房间。而“新分配的内存空间”,则像是一个“临时建筑物”。我们必须在不用它的时候,主动将它拆迁。拆迁的工作由delete来完成。

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

如:

int* p = new int;

*p = 100;

cout << *p << endl;

屏幕将输出100。

20.2.2 在new 时初始化内存的值

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

语法:

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

这样,上例可以改为:

int* p = new int(100);

cout << *p << endl;

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

char* pchar = new char('A');

20.2.3 delete

语法:

delete 指针变量;

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

举例:

int* p;

p = new int;

*p = 100;

cout << *p << endl;

delete p;

system("PAUSE");

注意,当一个指针接受delete操作后,它就又成了一个“指向不明”的指针。尽管我们可以猜测它还是指向“原来的房子”,然而,事实上,那座“房子”已经被delete “拆迁”掉了。

20.2.4 实验: new 和 delete

很简单的例子。

第一步:

首先,在CB新建一个控制台程序。然后把上一小节的代码放到main()函数内。运行。结果如下:

(new 和 delete)

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

第二步:

接下来我们来观察指针变量被delete之后,所指向的内存会是什么。但,这是一件犯了C、C++编程大忌的事:访问一个已经delete 的指针的值。如果你最近运气很差,你的CB可能会被强行退出。所以,你明白我们为什么要先存盘了,对不?

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

int* p;

p = new int;

*p = 100;

cout << *p << endl;

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

cout << *p << endl;  //我们故意去访问此时p所指的内存

system("PAUSE");

运行结果:

(访问delete之后的指针)

44244844??在你的机器可能不是这个数,但一定同样是怪怪的值。 原来是好端端的100,现在却成了44244844。不要问我这是为什么?昨天来时,美眉还住在这里一座别致小阁楼里,今日故地重游,这里竟成废墟一片,依稀只见破墙上尚有:“拆!——城建局”的字样?!

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

请大家自行在CB上完成本实验。我没有提供本题的实际工程。

20.2.5 new 和 delete 的关系

如果只有“建房”而没有“拆房”,那么程序就会占用内存越来越多。所以,当使用new 为某个指针分配出内存空间后,一定要记得在不需要再使用时,用delete 删除。下面是一个例子。演示new 和 delete 的对应使用。

//建屋和入住:

1) int* p = new int(100);

//使用:

2) cout << *p << endl;

//拆:

3) delete p;

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

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

第3句,释放内存(这4字节被系统收回准备做其它用途)。入住者呢?自然消失了。

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

char* a = new char('A');

cout << *a << endl;

*a = 'B';

cout << *a << end;

delete a;

bool* b = new bool;

*b = true;

if (*b)

cout << "true" << endl;

else

cout << "fale" << endl;

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

20.3 new [] 和 delete []

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

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

new [] 语法:

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

语法实例:

int* p = new int[20];

首先,你需要迅速回想一下,如果是 int* p = new int(20); 那么该是什么作用?否则你很容易在事后把二者混了。

实例中,用 new 申请分配了20个连续的整数所需的空间,即:20 * sizeof(int) = 80个字节。

图示为:

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

new int 只是分配了一个整数的内存空间,而 new int[N]却分配了N个整数的连续空间。看来,new[] 比 new “威力更猛”,所以,我们同样得记得:用 new [] 分配出空间,当不在需要时,必须及时调用 delete [] 来释放。

delete [] 语法:

delete [] 指针变量;

如:

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

int* p = new int[1000];

//然后使用这些空间:

……

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

delete [] p;

20.3.2 new []/ delete[] 示例

在 Windows XP 、Windows NT 或 Windows 2000中,按 Ctrl + Alt + Del (其它操作系统,如Windows98/Me等千万不要按些组合键,否则电脑将重启)。可以调出 Windows 任务管理器,其中要以看出当前粗略的的内存使用量。下面我们结合该工具,写一个程序,先分配100M的内存,再释放。

这是程序代码的抓图:

各步运行结果:

程序显示 任务管理器抓图

第一步:分配内存之前

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

第二步:分配了100兆的内存

(多出了100M)

第三步:又释放出这100兆

(回到207兆)

注意:使用 new 得来的空间,必须用 delete 来释放;使用 new [] 得来的空间,必须用 delete [] 来释放。彼此之间不能混用。

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

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

在 通过 new [] 指向连续空间以后,p 就变得和一个一维数组很是类似。我们先来复习一下数组相关知识。

假设是这么一个数组:

int arr[20];

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

(数组 arr 的内存示意)

和指针变量相比, 数组没有一个单独的内存空间而存放其内存地址。即:指针变量p是一个独立的变量,只不过它的值指向另一段连续的内存空间;而数组arr,本身代表的就是一段连续空间。

如果拿房间来比喻。指针和数组都是存放地址。只不过,指针是你口袋里的那本通讯录上写着的地址,你可以随时改变它的内容,甚至擦除。而数组是你家门楣上钉着的地址,你家原来是“复兴路甲108号”,你绝对不能趁月黑天高,把它涂改为“唐宁街10号”。

数组是“实”的地址,不能改变。当你和定义一个数组,则这个数组就得根据它在内存中的位置,得到一个地址,如上图中的“0x1A000000”。只要这个数组存在,那么它终生的地址就是这个值。

指针是一个“虚”的地址,可以改变地址的值。当你定义一个指针变量,这个变量占用4个字节的内存,你可以往这4字节的内存写入任意一个值,该值被当成一个内存地址。比如,你可以写入上面的“0x1A000000”,此时,指针p指向第一个元素。也可以改为“0x1A000003”,此时,指针p指向第二个元素。

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

我们来看实例,首先看二者的类似之处。下面左边代码使用数组,右边代码使用指针。

数组 指针 (通过 new [] 所得)
//定义:

int arr[20];

//定义:

int* p = new int[20];

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

arr[0] = 100;

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

p[0] = 100;

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

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

{

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

}

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

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

{

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

}

//输出所有元素:

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

{

cout << arr[i] << endl;

}

//输出所有元素:

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

{

cout << p[i] << endl;

}

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

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

cout << *(arr + 0) << endl;  //*(arr+0) 等于 *arr

cout << *(arr + 1) << endl;

cout << *(arr + 1) << endl;

输出结果:

100

150

200

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

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

cout << *(p + 0) << endl; //*(p + 0) 等于 *p

cout << *(p + 1) << endl;

cout << *(p + 1) << endl;

输出结果:

100

150

200

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

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

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

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

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

数组 指针
//定义并且初始化:

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

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

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

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

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

{

p[i] = i;

}

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

arr = arr + 1;  // 错!

cout << *arr << endl;

arr++;  // 错!

cout << *arr << endl;

arr--;  // 错!

cout << *arr << endl;

输出结果:

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

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

p = p + 1;

cout << *p << endl;

p++;

cout << *p << endl;

p--;

cout << *p << endl;

输出结果:

1

2

1

//释放空间:

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

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

//释放空间:

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

delete [] p;

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

接下来的问题也很重要。

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

指针通过 new 或 new[] ,向系统“申请”得到一段内存空间,我们说过,这段内存空间必须在不需要将它释放了。有点像人类社会的终极目标“共产主义”下的“按需分配”。需要了就申请,不需要了,则主动归还。

现在问题就在于这个“主动归还”。当然,指针并不存在什么思想觉悟方面的问题,说光想申请不想归还。真正的问题是,指针在某些方面的表现似乎有些像“花心大萝卜”。请看下面代码,演示令人心酸的一幕。

/*

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

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

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

*/

int* p = new int[100]; 

/*

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

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

*/

……

/*

p 改变指向 ---- 分手

*/

int girl [100];   //第三者出现

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

/*

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

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

惩罚他

*/

delete [] p;

扣除注释,上面只有4行代码。这4行代码完全符合程序世界的宪法:语法。也就是说对它们进行编译,编译器会认为它们毫无错误,轻松放行。

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

我们原意是要释放 p 最初通过 new int[100]而得到的内存空间,但事实上,p那时已经指向girl[100]了。结果,第一、最初的空间并没有被释放。第二、girl[100] 本由系统自行释放,现在我们却要强行释放它。

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

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

比如:

int* p = new int[3];

*p = 1;

cout << *p << endl;

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

*p = 2;

cout << *p << endl;

//错误的释放:

delete [] p;

在 delete [] p 时,p指向的是第二个元素,结果该释放将产生错位:第一个元素没有被释放,而在最后多删除了一个元素。相当你盖房时盖的是前3间,可以在拆房时,漏了头一间,从第二间开始拆起,结果把不是你盖的第4房间倒给一并拆了。

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

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

p--;

delete [] p;

但当我们的指针指向变化很多次时,在释放前要保证一步不错地一一退回,会比较困难。所以另一方法是在最初时“备份”一份。在释放时,直接释放该指针即可。

int* p = new int[3];

int* pbak = *p;    //备份

//移动 p

……

//释放:

delete [] pbak;

由于pbak正是指向p最初分配后的地址,我们删除pbak,就是删除p最初的指向。此时我们不能再删除一次p。这也就引出new / delete 及 new[] / delete[] 在本章的最后一个问题。

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

第一种情况,错了最直接:

int* p = new int(71);

cout << *p << endl;

delete p; //OK!

delete p; //ERROR! 重复删除p

当然,如果同一指针在delete之后,又通过new 或 new[] 分配了一次内存,则需要再删除一次:

int* p = new int(71);

cout << *p << endl;

delete p; //OK!

...

p = new int(81);

delete p; //OK!

...

p = new int[10];

for (int i=0; i<10; i++)

*p = i;

...

delete [] p; //OK!

上面代码中,共计三次对p进行delete 或 delete[],但不属于重复删除。因为每次delete都对应一次新的new。

我们下面所说的例子,均指一次delete之后,没有再次new,而重复进行delete。

第二种情况,重复删除同一指向的多个指针

int* p1 = new int(71);

int* p2 = p1;   //p2和p1 现在指向同一内存地址

cout << *p1 << endl;

cout << *p2 << endl;

delete p1;  //OK

delete p2;  //ERROR! p2所指的内存,已通过delete p1而被释放,不可再delete一次。

同样的问题,如果你先删除了p2,则同样不可再删除p1。

...

delete p2; //OK

delete p1; //ERROR

第三种情况,删除指向某一普通变量的指针

int a = 100;

int* p = &a;

delete p;  //ERROR

p 不是通过new 得到新的内存空间,而是直接指向固定变量:a。所以删除p等同要强行剥夺a的固有空间,会导致出错。

20.5 C 方式的内存管理

new/delete只在C++里得到支持。在C里,内存管理是通过专门的函数来实现。另外,为了兼容各种编程语言,操作系统提供的接口通常是 C 语言写成的函数声明 (Windows 本身也由C和汇编语言写成)。这样,我们就不得不同时学习C的内存管理函数。

20.5.1 分配内存 malloc 函数

需要包含头文件:

#include <alloc.h>

#include <stdlib.h>

函数声明(函数原型):

void *malloc(int size);

说明:malloc 向系统申请分配指定size个字节的内存空间。返回类型是 void* 类型。void* 表示未确定类型的指针。C,C++规定,void* 类型可以强制转换为任何其它类型的指针。

从函数声明上可以看出。malloc 和 new 至少有两个不同: new 返回指定类型的指针,并且可以自动计算所需要大小。比如:

int *p;

p = new int; //返回类型为int* 类型(整数型指针),分配大小为 sizeof(int);

或:

int* parr;

parr = new int [100];  //返回类型为 int* 类型(整数型指针),分配大小为 sizeof(int) * 100;

而 malloc 则必须由我们计算要字节数,并且在返回后强行转换为实际类型的指针。

int* p;

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

第一、malloc 函数返回的是 void * 类型,如果你写成:p = malloc (sizeof(int)); 则程序无法通过编译,报错:“不能将 void* 赋值给 int * 类型变量”。所以必须通过 (int *) 来将强制转换。

第二、函数的实参为 sizeof(int) ,用于指明一个整型数据需要的大小。如果你写成:

int* p = (int *) malloc (1);

代码也能通过编译,但事实上只分配了1个字节大小的内存空间,当你往里头存入一个整数,就会有3个字节无家可归,而直接“住进邻居家”!造成的结果是后面的内存中原有数据内容全部被清空。

malloc 也可以达到 new [] 的效果,申请出一段连续的内存,方法无非是指定你所需要内存大小。

比如想分配100个int类型的空间:

int* p = (int *) malloc ( sizeof(int) * 100 ); //分配可以放得下100个整数的内存空间。

另外有一点不能直接看出的区别是,malloc 只管分配内存,并不能对所得的内存进行初始化,所以得到的一片新内存中,其值将是随机的。

除了分配及最后释放的方法不一样以外,通过malloc或new得到指针,在其它操作上保持一致。

20.5.2 释放内存 free 函数

需要包含头文件(和 malloc 一样):

#include <alloc.h>

#include <stdlib.h>

函数声明:

void free(void *block);

即: void free(指针变量);

之所以把形参中的指针声明为 void* ,是因为free必须可以释放任意类型的指针,而任意类型的指针都可以转换为void *。

举例:

int* p = (int *) malloc(4);

*p = 100;

free(p); //释放 p 所指的内存空间

或者:

int* p = (int *) malloc ( sizeof(int) * 100 ); //分配可以放得下100个整数的内存空间。

……

free(p);

free 不管你的指针指向多大的空间,均可以正确地进行释放,这一点释放比 delete/delete [] 要方便。不过,必须注意,如果你在分配指针时,用的是new或new[],那么抱歉,当你在释放内存时,你并不能图方便而使用free来释放。反过来,你用malloc 分配的内存,也不能用delete/delete[] 来释放。一句话,new/delete、new[]/delete[]、malloc/free 三对均需配套使用,不可混用!

int* p = new int[100];

... ...

free(p);  //ERROR! p 是由new 所得。

这也是我们必须学习 malloc 与 free 的重要理由之一,有时候,我们调用操作系统的函数(Windows API)时,会遇到由我们的程序来分配内存,API函数来释放内存;或API函数来分配内存,而我们的程序来负责释放,这时,必须用malloc或free来进行相应的工作。

当然,保证所说的内存分配与释放方式不匹配的错误发生,Windows API函数也提供了一套专门的内存管理函数给程序员,为了不在这一章里放太多相混的内容,我们在Windows编程的课程再讲相关内容。

最后还有一个函数,也是我们要学习C方式的内存管理函数的原因。

20.5.3 重调空间的大小: realloc 函数

需要包含头文件(和 malloc 一样):

#include <alloc.h>

#include <stdlib.h>

函数声明:

void *realloc(void *block, int size);

block 是指向要扩张或缩小的内存空间的指针。size 指定新的大小。

realloc 可以对给定的指针所指的空间进行扩大或者缩小。size 是新的目标大小。比如,原来空间大小是40个字节,现在可以将size 指定为60,这样就扩张了20个字节;或者,将size 指定为20,则等于将空间缩小了20个字节。

无论是扩张或是缩小,原有内存的中内容将保持不变。当然,对于缩小,则被缩小的那一部分的内容会丢失。

举例:

//先用 malloc 分配一指针

int* p = (int *) malloc (sizeof(int) * 10);  //可以存放10个整数

……

//现在,由于些某原因,我们需要向p所指的空间中存放15个整数

//原来的空间不够了:

p = (int *) realloc (p, sizeof(int) *15); //空间扩张了 (15 - 10) * sizeof(int) = 20 个字节

……

//接下来,我们决定将p所指内存空间紧缩为5个整数的大小:

p = (int *) realloc (p, sizeof(int) * 5); //缩小了 (15 - 5) * sizeof(int) = 40 个字节

……

free (p);

这么看起来,realloc 有点像是施工队对一个已建的房屋进行改修:可以将房间后面再扩建几间,也可以拆掉几间。不管是扩还是拆,屋里原来的东西并不改变。

不过,这里要特别提醒一点:这个施工队有时会做这种事:1、在一块新的空地上新建一座指定大小的房屋;2、接着,将原来屋子里的东西原样照搬到新屋;3、拆掉原来的屋子。

这是什么指意呢?

realloc 并不保证调整后的内存空间和原来的内存空间保持同一内存地址。相反,realloc 返回的指针很可能指向一个新的地址。

所以,在代码中,我们必须将realloc返回的值,重新赋值给 p :

p = (int *) realloc (p, sizeof(int) *15);

甚至,你可以传一个空指针(0)给 realloc ,则此时realloc 作用完全相当于malloc。

int* p = (int *) realloc (0,sizeof(int) * 10);  //分配一个全新的内存空间,

这一行,作用完全等同于:

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

20.5.4 malloc、realloc、free的例子

打开CB6,新建一空白控制台工程。

第一步:在 Unit1.cpp 中的最前面,加入引用 alloc.h 等头文件的代码:

……

#pragma hdrstop

#include <alloc.h> //三个函数的声明都这个头文件里

#include <iostream.h>

……

第二步:将以下代码加入主函数 main 中间:

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

cout << "realloc 之前p指向的内存地址: " << p << endl;

for (int i=0; i<10; i++)

{

p[i] = i + 1;

}

cout << "realloc 之前p指向的内存中的内容:" << endl;

for (int i=0; i<10; i++)

{

cout << p[i] << ",";

}

cout << endl;

p = (int *) realloc (p, sizeof(int) * 15);

cout << "realloc 之后p指向的内存地址: " << p << endl;

cout << "realloc 之后p指向的内存中的内容:" << endl;

for (int i=0; i<15; i++)

{

cout << p[i] << ",";

}

cout << endl;

free (p);

system("PAUSE");

运行结果:

(malloc, realloc, free 上机题)

从图中我们看到 realloc 前后的p指向的内存地址同样是 9647936 。但记住,这并不是结论,真正的结论我们已经说过“realloc 并不保证返回和原来一样的地址。”所谓的“并不保证”的意思是:“我尽力去做了,但仍然有可能做不到。”。

无论调用几次 realloc,最后我们只需一次 free。

 

为指针分配和释放空间相关推荐

  1. 指针分配和释放空间(转)

    指针分配和释放空间(转) (2012-06-06 12:42:04) 转载▼ 标签: 指针 分类: C/Cplusplus 20.1 理解指针的两种"改变" 普通变量(非指针,简单 ...

  2. 第二十章 指针 二 为指针分配和释放空间(转)

    载自<白话c++>:http://17de.com/library/CPP/ls20.htm 20.1 理解指针的两种"改变" 20.1.1 改变指针的值 20.1.2 ...

  3. 【C 语言】二级指针作为输入 ( 自定义二级指针内存 | 为 二级指针 分配内存 - 存放 一维指针 | 为每个 一级指针 分配内存 | 释放二维指针内存 )

    文章目录 一.二级指针 1.为 二维指针 分配内存 2.为每个 一维指针 分配内存 3.释放 二维指针 内存 二.完整代码示例 一.二级指针 声明二级指针 : // 声明二维指针char **p = ...

  4. 【C 语言】结构体 ( 结构体中嵌套一级指针 | 分配内存时先 为结构体分配内存 然后再为指针分配内存 | 释放内存时先释放 指针成员内存 然后再释放结构头内存 )

    文章目录 一.结构体中嵌套一级指针 1.声明 结构体类型 2.为 结构体 变量分配内存 ( 分配内存时先 为结构体分配内存 然后再为指针分配内存 ) 3.释放结构体内存 ( 释放内存时先释放 指针成员 ...

  5. 走进C++程序世界-----指针(动态申请空间和释放空间)

    使用关键字NEW分配内存 在讲解分配内存之前,首先来理解下内存区划分.内存分配.常量存储区.堆.栈.自由存储区.全局区这些也是初学者容易混淆的地方.我的博客中有一篇文章描述C语言中代码分配情况,点击链 ...

  6. 【C 语言】结构体 ( 结构体中嵌套二级指针 | 为 结构体内的二级指针成员 分配内存 | 释放 结构体内的二级指针成员 内存 )

    文章目录 一.结构体中嵌套二级指针 1.结构体中嵌套二级指针 类型声明 2.为 结构体内的二级指针成员 分配内存 3.释放 结构体内的二级指针成员 内存 二.完整代码示例 一.结构体中嵌套二级指针 1 ...

  7. Delphi 的内存操作函数(2): 给数组指针分配内存

    静态数组, 在声明时就分配好内存了, 譬如: vararr1: array[0..255] of Char;arr2: array[0..255] of Integer; beginShowMessa ...

  8. C/C++动态二维数组的内存分配和释放

    C语言: 1 //二维数组动态数组分配和释放 2 //数组指针的内存分配和释放 3 //方法一 4 char (*a)[N];//指向数组的指针 5 a = (char (*)[N])malloc(s ...

  9. 如何分配和释放存储空间

    new是c++中一个关键字,他的使用操作的结果就是申请一段指定数据类型大小的内存, 使用方式: 指针变量=new 数据类型, 实例: int *p;       p=new int; p=new in ...

最新文章

  1. 论NLP领域的内卷:我配不配找工作?
  2. 用chkconfig配置linux自启动服务
  3. Python程序反转给定数字(2种不同方式)
  4. 【国际专场】laravel多用户平台(SaaS, 如淘宝多用户商城)的搭建策略
  5. 架构设计 例子和实践
  6. web.config文件之自定义错误节
  7. 6还是5?大还是小?看完这些动图,感觉数学白学了
  8. 《从单体迈向 Serverless 的避坑指南》
  9. 使用PyCharm连接云主机教程
  10. java内存stack heap_java内存解析-------stack(栈)和heap(堆)的理解
  11. 文字描边_巧用Illustrator“3D”和“凸出和斜角”功能,制作炫酷立体文字
  12. 讨论 innodb_log_block_size 变量
  13. atitit。win7 win8 win9 win10 win11 新特性总结与战略规划
  14. 【Elasticsearch源码】 GET分析
  15. html之圆形用户头像
  16. 网站管理后台帐号密码找回方法
  17. 解决 cannot resolve 依赖包的问题
  18. Navicat 15.0.27 激活时弹出No All Pattern Found File Already Patched?(已解决)
  19. navicat oracle存储过程,Navicat 运行 Oracle 存储过程示例
  20. 基于MAC地址划分VLAN

热门文章

  1. BIOS控制降频温度设置
  2. 看论文时的几个数学术语
  3. CPinyin unicode汉字查找拼音(支持多音字)
  4. Java中Ascall码的使用
  5. 类型转换——int转换成char(截短)
  6. ffmpeg实现摄像头拉流_利用ffmpeg一步一步编程实现摄像头采集编码推流直播系统...
  7. ASP完美优化(不断更新)
  8. 谷歌想要返华,但却陷入了尴尬境地
  9. 鼠标移入图片高亮,其余颜色变暗
  10. html正则邮箱格式,JS正则表达式判断邮箱格式是否正确