- 浏览: 1007719 次
- 性别:
- 来自: 广州
文章分类
- 全部博客 (394)
- OSGI (14)
- 多线程 (10)
- 数据库 (30)
- J2ME (1)
- JAVA基础知识 (46)
- 引用包 (1)
- 设计模式 (7)
- 工作流 (2)
- Ubuntu (7)
- 搜索引擎 (6)
- QT (2)
- Ubuntu下编程 (1)
- 小程序 (2)
- UML (1)
- Servlet (10)
- spring (16)
- IM (12)
- 文档视频转为flash格式在线播放 (19)
- Maven (8)
- 远程调用 (2)
- PHPRPC (1)
- EXTJS学习 (2)
- Hibernate (16)
- 技术文章 (38)
- flex (5)
- 海量数据处理 (5)
- FTP (8)
- JS (10)
- Struts (1)
- hibernate search (13)
- JQuery (2)
- EMail (3)
- 算法 (4)
- SVN (7)
- JFreeChart (4)
- 面试 (4)
- 正规表达式 (2)
- 数据库性能优化 (10)
- JVM (6)
- Http Session Cookie (7)
- 网络 (12)
- Hadoop (2)
- 性能 (1)
最新评论
-
hy1235366:
能够随便也发一下,你退火算法程序使用的DistanceMatr ...
模拟退火算法总结(含例子)(转) -
梅强强:
感谢分享。。帮大忙了
swftools转换文件时线程堵塞问题的解决方法 -
wenlongsust:
openoffice和文件不在同一个服务器上,用过吗?
[JODConverter]word转pdf心得分享(转) -
2047699523:
如何在java Web项目中开发WebService接口htt ...
利用Java编写简单的WebService实例 -
abingpow:
唉,看起来好像很详细很不错的样子,可惜不是篇面向初学者的文章, ...
Spring与OSGi的整合(二)(转)
载自<白话c++>:http://17de.com/library/CPP/ls20.htm
20.2 C++ 方式的内存分配与释放 new 和 delete
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。
发表评论
-
Java线程(二):线程同步synchronized和volatile(转)
2014-03-17 00:09 914转载自:http://blog.csdn.net/ghsau ... -
浅谈Java多线程的同步问题(l转)
2014-03-17 00:07 955非常好的使用线程同步的文章 转载自http://www.c ... -
JVM的垃圾回收机制详解和调优(转)
2013-06-20 10:31 7401.JVM的gc概述 gc即垃圾 ... -
深入探讨 Java 类加载器(转)
2013-06-20 10:17 898转载自:http://www.ibm.com/develop ... -
java反射详解(推荐转)
2013-05-15 10:42 903载自:http://www.cnblogs.com/roll ... -
java静态方法、非静态代码块{}、静态代码块static{}(转)
2012-07-13 14:33 1511转自:http://www.cn-java.com/www1/ ... -
error 与 Exception区别(转)
2012-07-13 14:31 1100Error类和Exception类都继承自Throwab ... -
java面试题有哪些常见的(转)
2012-07-13 14:30 1219第一,谈谈final, finally, finaliz ... -
java Math.round()(转)
2012-07-11 14:17 1210public class MathTest { ... -
Java异常的分类(转)
2012-07-11 08:57 1067转载自:http://blog.csdn.net/ilibab ... -
java 内联函数(转)
2012-06-28 23:40 1850以前用过C++,知道它 ... -
堆栈,堆栈,堆和栈的区别(转)
2011-05-08 00:36 1207不防看看这篇文章:http://www.cppblog.com ... -
数据结构中各种时间复杂度(转)
2011-05-06 09:58 2810(1)冒泡排序 冒泡排序就是把小的元素往 ... -
C#之int挑战Java之Integer(转)
2011-04-28 14:24 1359可能有些图会看不到,可以到转载处去阅读:http://kb.c ... -
Java: 堆 & 栈(转)
2011-04-28 14:16 1419栈是运行时的单位,而堆是存储的单位。栈解决程序的运行问题,即程 ... -
native2ascii 使用方法 及 Java字符编码(转)
2011-04-18 01:17 2752在做Java开发的时候,常 ... -
Unicode,ISO-8859,GBK,UTF-8编码及相互转换(java)(转)
2011-04-18 01:15 69071、函数介绍在Java中,字符串用统一的Unicode编码 ... -
GBK与UTF-8 转换乱码详解(转)
2011-04-18 01:07 3507getBytes 的功能是将字符转换成字节数组, gbk. ... -
Java TreeMap的简单实现(转)
2011-04-18 00:29 1118TreeMap的实现与二叉搜索树显示,其对应的节点格式为 E ... -
Java乱码问题分析(转)
2011-04-17 02:22 1525java采用unicode编码来处理字符。Java程序无论是从 ...
相关推荐
"C、C++语言指针二为指针分配和释放空间" 本章主要讲解C、C++语言中的指针概念,包括指针的两种改变、内存分配和释放空间。 20.1 理解指针的两种“改变” 在C、C++语言中,普通变量(非指针)只能改变值,而指针...
1. **创建新节点**:使用`malloc`或`calloc`动态分配内存,为新节点C分配空间,并初始化其数据域和指针为NULL。 2. **定位插入位置**:根据题目要求,确定节点C应该插入到哪个位置。这可能需要遍历链表,找到前一个...
### 关于C++动态为二维数组分配空间的问题 在C++编程中,动态分配内存是一项非常重要的技能,尤其是在处理不确定大小的数据结构时。本文将详细解释如何使用`new`操作符来动态分配二维数组的空间,并通过示例代码...
2. **动态内存管理**:指针支持动态分配和释放内存,这对于处理不确定大小的数据结构尤为重要。 3. **灵活的数据结构支持**:指针是实现链表、树等动态数据结构的基础。 4. **参数传递**:通过指针传递参数,可以在...
在C语言中,指针是程序设计中的一个重要概念,它允许程序员直接操作内存地址,从而实现更高效和灵活的编程。本课件主要探讨了C语言中的指针使用及其相关概念,适合初学者掌握。 首先,我们要理解变量与地址的关系。...
在C语言中,指针是其强大的特性之一,它允许我们直接操作内存地址,从而实现高效、灵活的编程。...通过学习第八章的内容,我们可以更好地理解如何声明、初始化和使用指针,以及它们在程序设计中的重要性。
// ptr现在指向数组的第二个元素 ``` 指针在函数参数传递中也非常有用。通过传递指针,函数可以直接修改调用者提供的变量,而不是复制变量的副本。此外,多级指针(如`int **`)允许我们指向指针本身,这在处理...
- **添加工程师**:可以使用指针来动态地为新加入的工程师分配内存空间。 - **删除工程师**:利用指针可以方便地释放不再需要的工程师信息所占用的内存。 - **查找工程师**:通过指针可以快速定位到特定工程师的...
2. **动态内存分配**:通过指针可以申请和释放内存,这对于管理动态数据结构非常重要。 3. **函数参数传递**:使用指针作为函数参数可以实现对原始数据的修改,无需返回值即可改变调用函数中的变量值。 #### 指针...
- **数据存储**:当定义一个变量时,系统会根据变量的类型为其分配一定的内存空间,并且每个变量都有一个确定的地址,通常是起始地址。 #### 三、指针变量的声明与使用 ##### 3.1 声明指针变量 - **语法**:`类型...
### C++ 从头开始:深入理解指针...通过本文的介绍,希望能够帮助初学者建立起对指针的基本认识,为进一步学习复杂的指针应用和高级特性奠定坚实的基础。记住,实践是最好的老师,多动手写代码,才能真正掌握这些概念。
根据提供的文件信息,标题与描述均为“C++指针方面的心得”,这表明文章将主要围绕C++语言中的指针概念及其使用技巧进行探讨。由于标签部分与内容并不相关,因此我们将忽略这部分信息,直接从标题和描述中提取并展开...
对于每个新节点,我们需要为结构体分配内存,以存储字符和指针。 7. **链接节点**:分配内存后,需要更新新节点的next指针,使其指向链表中的下一个节点。如果链表为空,新节点就是头节点,即`head = newNode`;...
例如,对于整型数组`int arr[5]`,`arr+1`将指向数组的第二个元素。 五、指针与函数 指针作为函数参数使用时,可以实现在函数内部修改外部变量的值。这在处理大型数据结构或避免不必要的数据复制时非常有用。例如,...
- **动态内存分配**:使用指针结合`malloc()`和`free()`等函数,可以实现动态内存的申请和释放,这对于大型项目非常重要。 #### 结论 指针是C语言中极为重要的一个概念,掌握好指针不仅可以提高编程效率,还能更好...
指针与`malloc()`、`calloc()`、`realloc()`和`free()`函数一起工作,用于动态地分配和释放内存。例如: ```c int *arr = malloc(5 * sizeof(int)); // 分配5个整数的空间 free(arr); // 释放内存 ``` 通过...
在C++编程语言中,指针是至关重要的概念,它为程序员提供了对内存直接操作的能力。本文将深入探讨C++指针的使用方法,并总结一些关键知识点,帮助开发者更好地理解和运用这一强大的工具。 首先,我们要了解什么是...
而动态内存分配则允许在运行时根据需要分配和释放内存,这为程序员提供了更大的灵活性。 8.1 动态内存分配的概念和方法 动态内存分配通过`new`和`delete`运算符来完成。`new`用于请求内存,而`delete`用于释放已...
例如,对于数组`int arr[5]`,`arr+1`将指向数组的第二个元素。不过,需要注意的是,指针加减运算的步长取决于其指针类型。 5. **多级指针** C/C++支持多级指针,如`int **pp`是一个二级指针,它可以指向一个指向...
例如,可以使用`malloc`函数为指针分配内存: ```c char *p = malloc(10); // 分配10个字节的内存 ``` 分配内存后,最好将其初始化为已知值,通常初始化为0。这可以通过`memset`函数完成: ```c char *p = malloc...