第二十一章指针三实例演练与提高 21.1简单变量、数组、指针 21.2小王成绩管理系统V2.0的问题 软件升级历史 21.3指针的最常用用法 21.3.1分配内存 21.3.2访问指针指向的内存 21.4小王成绩管理系统V3.0 21.5字符串指针 21.5.1为字符串分配指定大小的空间 21.5.2字符串常用函数 21.5.2.1字符串比较 5.2.2字符串复制 1.6指针数组 21.6.1什么叫“指针数组”? 21.6.2指针数组实例 21.6.3指针数组实例二 21.6.4字符串指针数组 21.1简单变量、数组、指针 学习的知识点越来越多了 刚开始会觉得很兴奋啊,学得越多越好嘛。可慢慢的就会感到压力了,各种知识点在头脑里 混在一起,每个都变得模糊了。 其实,每个知识点都有它存在,或出现的理由,只要我们多做对比,就会发现学习的知识点 越多,反倒越容易理解每个知识点本质
第二十一章 指针 三 实例演练与提高 21.1 简单变量、数组、指针 21.2 小王成绩管理系统 V2.0 的问题 21.2.1 软件升级历史 21.3 指针的最常用用法 21.3.1 分配内存 21.3.2 访问指针指向的内存 21.4 小王成绩管理系统 V3.0 21.5 字符串指针 21.5.1 为字符串分配指定大小的空间 21.5.2 字符串常用函数 21.5.2.1 字符串比较 21.5.2.2 字符串复制 21.6 指针数组 21.6.1 什么叫“指针数组”? 21.6.2 指针数组实例一 21.6.3 指针数组实例二 21.6.4 字符串指针数组 21.1 简单变量、数组、指针 学习的知识点越来越多了…… 刚开始会觉得很兴奋啊,学得越多越好嘛。可慢慢的就会感到压力了,各种知识点在头脑里 混在一起,每个都变得模糊了。 其实,每个知识点都有它存在,或出现的理由,只要我们多做对比,就会发现学习的知识点 越多,反倒越容易理解每个知识点本质
比如说,简单变量、数组、指针,三者都是C++中用于表达数据的工具,但在表达能力上, 又各有不同 如果用建筑上的房间来比喻 简单变量是一间房屋。优点是占用空间少,建筑时间短,缺点是一间房子只适于住一个人 数组是房间数固定的一排房子,每个房子里头同样只住一人,但由于它有多间,所以适于多 人居住,优点是可以统一管理多人,缺点一来是占用空间大,二来房间数一旦确定,就不能改 变了。先头盖了10间,如果如果来了11个人,就有一人住不下,如果来了9个人,就有一间 浪费 指针呢……它不是实际房子,而是设计纸上的房子。因此,它首先有一个特点:如果你想让 指针存储数据,那一定得先为它分配内存。这就像光有一张设计蓝图是解决不了四代同堂的问 题的,重要的是你还得根据这张蓝图,去找块地皮盖好房子。指针的优点是可以临时决定要盖 多少间房子 下面我们回顾一个例子,以理解三者的不同用处 21.2小王成绩管理系统V2.0的问题 先回顾一下该程序的升级过程,今天我们将对它做出两种不同方向的改进 21.2.1软件升级历史 V1.0:本版成绩管理系统实现让计算机自动统计6个班级的成绩总分和平均分。 V2.0:经过改进,本版可以实现多达5000个学生的成绩进行求总分和平均分,并且可以支 持用户输入序号,查询任意一个学生的成绩! 在第一版,小王正在学习“循环流程”。通过在每次循环中,让用户输入一个成绩,然后保 存在一个简单变量里,并累加到另一个简单变量,最终计算出总分和平均分。 第二版,由于段长要求不仅可以统计5000个学员的成绩,而且应实现成绩查询功能,这就 要求程序必须同时记下5000个学生成绩。小王先是想用5000个简单变量来记下成绩一一这显
比如说,简单变量、数组、指针,三者都是 C++中用于表达数据的工具,但在表达能力上, 又各有不同。 如果用建筑上的房间来比喻: 简单变量是一间房屋。优点是占用空间少,建筑时间短,缺点是一间房子只适于住一个人; 数组是房间数固定的一排房子,每个房子里头同样只住一人,但由于它有多间,所以适于多 人居住,优点是可以统一管理多人,缺点一来是占用空间大,二来房间数一旦确定,就不能改 变了。先头盖了 10 间,如果如果来了 11 个人,就有一人住不下,如果来了 9 个人,就有一间 浪费。 指针呢……它不是实际房子,而是设计纸上的房子。因此,它首先有一个特点:如果你想让 指针存储数据,那一定得先为它分配内存。这就像光有一张设计蓝图是解决不了四代同堂的问 题的,重要的是你还得根据这张蓝图,去找块地皮盖好房子。指针的优点是可以临时决定要盖 多少间房子。 下面我们回顾一个例子,以理解三者的不同用处。 21.2 小王成绩管理系统 V2.0 的问题 先回顾一下该程序的升级过程,今天我们将对它做出两种不同方向的改进。 21.2.1 软件升级历史 V1.0 : 本版成绩管理系统实现让计算机自动统计 6 个班级的成绩总分和平均分。 V2.0 : 经过改进,本版可以实现多达 5000 个学生的成绩进行求总分和平均分,并且可以支 持用户输入序号,查询任意一个学生的成绩! 在第一版,小王正在学习“循环流程”。通过在每次循环中,让用户输入一个成绩,然后保 存在一个简单变量里,并累加到另一个简单变量,最终计算出总分和平均分。 第二版,由于段长要求不仅可以统计 5000 个学员的成绩,而且应实现成绩查询功能,这就 要求程序必须同时记下 5000 个学生成绩。小王先是想用 5000 个简单变量来记下成绩——这显
然太不实际了,后来学到数组,用数组轻松解决了这个问题,因为数组正是为“同时存储多个 相同类型的变量”这一问题来设计的 然而,第二版存在的不足也是显然易见的。那就是,它固定只能处理最多5000个学员的成 绩。假想,这个软件要推广到全市300个学校,每个学校的学生总数都是不一样的,更惨的是 每一年,一个学校的学生个数总是会有变化。难道就让我们的王老师时不时地改它的程序? 在没有指针时,惟一办法就是,浪费一点,比如定义数组元素个数为1万。目的是宁可浪费 点,也尽量不要出现不够的情况。显然,本办法只能算是一个无奈之举。难道就没有一个办 法,即可以适应某个山区小学只有30名学员也情况,又可以轻松对付某大学高达2万名学员的 情况? 锣声响起,锵锵锵……指针出场了。 指针是如何完成这一历史使命?带着问题,我们来学习下面的内容 我们会在学习新内容之中,同时有选择地做一些旧知识点的复习工作。但如果你仍看不懂下 面的一些代码,那得全面复习前两章的指针内容:或者,如果你连for都有些陌生,那你得重 温一下小王成绩管理系统的前两个版本 21.3指针的最常用用法 21.3.1分配内存 如何为指针分配和释放内存,上一章的内容中讲到了C+独用的new/ delete、new[]/ delete[]和C使用的 malloc, realloc/free方法。如果你忘了,请先复习。我们这里使用 C++的方法演练。 new只能为我们分配一个简单变量的内存,就是说new只盖了一间房子。new[]才能为我 们盖出一排的房子 例子
然太不实际了,后来学到数组,用数组轻松解决了这个问题,因为数组正是为“同时存储多个 相同类型的变量”这一问题来设计的。 然而,第二版存在的不足也是显然易见的。那就是,它固定只能处理最多 5000 个学员的成 绩。假想,这个软件要推广到全市 300 个学校,每个学校的学生总数都是不一样的,更惨的是 每一年,一个学校的学生个数总是会有变化。难道就让我们的王老师时不时地改它的程序? 在没有指针时,惟一办法就是,浪费一点,比如定义数组元素个数为 1 万。目的是宁可浪费 一点,也尽量不要出现不够的情况。显然,本办法只能算是一个无奈之举。难道就没有一个办 法,即可以适应某个山区小学只有 30 名学员也情况,又可以轻松对付某大学高达 2 万名学员的 情况? 锣声响起,锵锵锵……指针出场了。 指针是如何完成这一历史使命?带着问题,我们来学习下面的内容。 我们会在学习新内容之中,同时有选择地做一些旧知识点的复习工作。但如果你仍看不懂下 面的一些代码,那得全面复习前两章的指针内容;或者,如果你连 for 都有些陌生,那你得重 温一下小王成绩管理系统的前两个版本。 21.3 指针的最常用用法 21.3.1 分配内存 如何为指针分配和释放内存,上一章的内容中讲到了 C++独用的 new/delete、new[] / delete[] 和 C 使用的 malloc, realloc/ free 方法。如果你忘了,请先复习。我们这里使用 C++的方法演练。 new 只能为我们分配一个简单变量的内存,就是说 new 只盖了一间房子。new [] 才能为我 们盖出一排的房子。 例子:
int*p;//定义一个整型指针 p=new[10];//new[]为我们分配出10个int大小的内存。(盖了10间房,每间住 个整数 21.3.2访问指针指向的内存 前面:p=new[10]:为我们分配了10个int,那么,我们该如何设置和访问这10个整 数的值呢? 这一点完全和数组一致,我们来看数组是如何操作: inta[10];//以数组方式来定义10个int //让第1个整数的值为100 //让第2个整数的值为80 a[1]=80 指针的操作方式如下 int*p= new int[10];//定义1个整型指针,并为它分配出10个int的空间 //让第1个整数的值为100: p[0]=100 //让第2个整数的值为80 对比以上两段代码,你可以发现,对指针分配出的元素操作,完全和对数组的元素操作一致 不过,指针还有另一种对其元素的操作方法
int* p; //定义一个整型指针 p = new [10]; //new [] 为我们分配出 10 个 int 大小的内存。(盖了 10 间房,每间住 一个整数) 21.3.2 访问指针指向的内存 前面:p = new [10]; 为我们分配了 10 个 int,那么,我们该如何设置和访问这 10 个整 数的值呢? 这一点完全和数组一致,我们来看数组是如何操作: i n t a [10]; / /以数组方式来定义 10 个 int / /让第 1 个整数的值为 100: a [ 0] = 1 00; / /让第 2 个整数的值为 80: a [ 1] = 8 0; 指针的操作方式如下: i n t* p = n e w i nt[10]; / /定义 1 个整型指针,并为它分配出 10 个 int 的空间 / /让第 1 个整数的值为 100: p [ 0] = 1 00; / /让第 2 个整数的值为 80: p [ 1] = 8 0; 对比以上两段代码,你可以发现,对指针分配出的元素操作,完全和对数组的元素操作一致。 不过,指针还有另一种对其元素的操作方法:
int*p= new int[10];//定义1个整型指针,并为它分配出10个int的空间 //让第1个整数的值为100 (p+0)=100; //让第2个整数的值为80 (p+1)=8 请大家自己对比,并理解。如果觉得困难,请复习第19章关于*的用法,和指针偏移部分的 内容 21.4小王成绩管理系统V3.0 3.0版的最重要的改进就是:用户可以事先指定本校的学生总数 请仔细看好。 /定义一个指针,用于存入未知个数学生的成绩 //总成绩,平均成绩 int zcj=o, pjcj //首先,要求用户输入本校学生总数 int xss //学生总数 cout<<"请输入本校学生总数:";
i n t* p = n e w i nt[10]; / /定义 1 个整型指针,并为它分配出 10 个 int 的空间 / /让第 1 个整数的值为 100: *(p +0) = 1 00; / /让第 2 个整数的值为 80: *(p +1) = 8 0; 请大家自己对比,并理解。如果觉得困难,请复习第 19 章关于*的用法,和指针偏移部分的 内容。 21.4 小王成绩管理系统 V3.0 3.0 版的最重要的改进就是:用户可以事先指定本校的学生总数。 请仔细看好。 //定义一个指针,用于存入未知个数学生的成绩: int* pCj; //总成绩,平均成绩: int zcj=0, pjcj; //首先,要求用户输入本校学生总数: int x s zs; //学生总数 cout > x s zs;
//万一有调皮用户输入不合法的总数,我们就不处理 f(xszs =0) cout〉pCj[i] //输入数组中第i个元素 //不断累加总成绩: zcj+= pCj[i] //平均成绩 j= zcj/ xss //输出 cout<"总成绩:"<<zcj<<endl cout<"平均成绩:"<<pjcj<<end1 //下面实现查询
//万一有调皮用户输入不合法的总数,我们就不处理 if (x s zs > pCj[i]; //输入数组中第 i 个元素 //不断累加总成绩: zcj += pCj[i]; } //平均成绩: pjcj = zcj / x s zs; //输出: cout << "总成绩:" << zcj << endl; cout << "平均成绩:" << pjcj << endl; //下面实现查询: int i;
cout> i if(i >=1 &&i<=xszs cout<<cj[i-1]<end1;//问:为什么索引是i-1,而不是i? lse if(i !=0) cout<<"您的输入有误!"<endl while(i!=0);//用户输入数字0,表示结束。 //最后,要释放刚才分配出的内存: delete pCj 请大家现在就动手,实现小王成绩管理3.0版。这是本章的第一个重点。通过该程序,你应 该可以记住什么叫“动态分配内存” 21.5字符串指针 21.5.1为字符串分配指定大小的空间
do { cout > i; if( i >= 1 && i <= x s zs) { cout << cj[i-1] << endl; //问:为什么索引是 i-1,而不是 i ? } else if( i != 0) { cout << "您的输入有误!" << endl; } } while(i != 0); //用户输入数字 0,表示结束。 //最后,要释放刚才分配出的内存: delete [] pCj; ...... 请大家现在就动手,实现小王成绩管理 3.0 版。这是本章的第一个重点。通过该程序,你应 该可以记住什么叫“动态分配内存”。 21.5 字符串指针 21.5.1 为字符串分配指定大小的空间
有必要的话,你应复习一下第16章之第6节:字符数组 假设有个老外叫"Mike",以前我们用字符数组来保存,需要指定是5个字符大小的数组: char name[5]="Mike Mike"长4个字符,为什么要5个字符的空间来保存?这是因为计算机还需要为字符串最 后多保存一个零字符:’0。用来表示字符串结束了 在学了指针以后,我们可以用字符串指针来表达一个人的姓名: char* pname="Mike 此时,由系统自动为 pname分配5个字符的位置,并初始化为"Mike"。最后一个位置仍 然是零字符:“\0’。 采用字符串的好处,同样前面所说的,可以在程序中临时决定它的大小(长度) 比如 har* pname= new char[9];//临时分配9个字符的大小。 除了要记得额外为字符串的结束符’\0’分配一个位置以外,字符串指针并没有和其它指定有 太多的不同 既然讲到字符串,我们就顺带讲几个常用的字符串操作函数 21.5.2字符串常用函数
有必要的话,你应复习一下第 16 章之第 6 节:字符数组。 假设有个老外叫 "Mike",以前我们用字符数组来保存,需要指定是 5 个字符大小的数组: char name[5] = "Mike"; "Mike"长 4 个字符,为什么要 5 个字符的空间来保存? 这是因为计算机还需要为字符串最 后多保存一个零字符:'\0'。用来表示字符串结束了。 在学了指针以后,我们可以用字符串指针来表达一个人的姓名: char* pname = "Mike"; 此时,由系统自动为 pname 分配 5 个字符的位置,并初始化为 "Mike"。 最后一个位置仍 然是零字符:‘\0’。 采用字符串的好处,同样前面所说的,可以在程序中临时决定它的大小(长度)。 比如: char* pname; pname = new char[9]; //临时分配 9 个字符的大小。 除了要记得额外为字符串的结束符'\0'分配一个位置以外,字符串指针并没有和其它指定有 太多的不同。 既然讲到字符串,我们就顺带讲几个常用的字符串操作函数 21.5.2 字符串常用函数
字符串操作函数的声明都包含在该头文件: 21.5.21字符串比较 int strcmp (const char *sl, const char *s2) 较s1和s2两个字符串,返回看哪个字符串比较大。对于字母,该比较区分大小写 返回值: :s1 #include iostream. h
字符串操作函数的声明都包含在该头文件: 2 1 .5.2.1 字符串比较 int strcmp(const char *s1, const char *s2); 比较 s1 和 s2 两个字符串,返回看哪个字符串比较大。对于字母,该比较区分大小写 返回值: 0 : s1 > s2; int strcmpi(const char *s1, const char *s2); 该函数类似于上一函数,只是对于字母,它不区分大小写,比如它认为'A'和'a' 是相等的。 要说两个字符串相等不相等,还好理解,比如: "Borland" 和 "Borlanb" 显然不相等。不 过,字符串之间还有大小之分吗? 对于字母,采用 ASCII 值来一个个比较。谁先出现一个 ASCII 值比较大的字母,谁就是大者。 比如:"ABCD" 比 "AACD"大。 如果一直相等,但有长短不一,那就长的大。比如:“ABCD” 比 “ABC”。 记住了,由于在 ASCII 表里,小写字母比大写字母靠后,所以小写的反倒比大写的大。比如: "aBCD"比"ABCD"大啊。 我这里写个例子,看如何比较字符串: #include #include
int reu strcmp("ABCD","AACD") if (reu>0) coutAACD"<<endl cout<<"搞错了吧?"<<end1; 请大家照此例,分别比较"ABCD"和"ABC”、"aBCD"和"ABCD"。 如果你对如何用C+← Builder建立一个控制台下的工程,请复习第二章第3节 前面说的是英文字母,对于汉字字符串的比较,大小是如何确定的呢? 对于常用汉字, Windows按其拼音进行排序,比如“啊”是最小的,排在最前面,而“坐” 之类的,则比较大,排在后面 对于非常用的汉字,则按笔划来排序。有关常用不常的划分,是国家管的事,我们就不多说 我一直在网上叫“南郁”,大家可以拿你的名字和我做一下 strcmp,看看谁的名字比较大 (友情提醒:名字大没有什么好处,相反,名字大了,在各种场合里,一般是排名靠后的……) 21.5.22字符串复制 char *strcpy(char *dest, const char *src) 该函数用于将字符串src的内容,复制给字符串dest。注意,一定要保证dest有足够 的空间 该函数最后返回dest 比如 har name1[10]
int reu = strcmp ("ABCD", "AACD"); if (reu > 0) cout AACD" << endl; else cout << "搞错了吧?" << endl; 请大家照此例,分别比较 "ABCD" 和 "ABC" 、 "aBCD" 和 "ABCD"。 如果你对如何用 C++ Builder 建立一个控制台下的工程,请复习第二章第 3 节。 前面说的是英文字母,对于汉字字符串的比较,大小是如何确定的呢? 对于常用汉字,Windows 按其拼音进行排序,比如“啊”是最小的,排在最前面,而“坐” 之类的,则比较大,排在后面。 对于非常用的汉字,则按笔划来排序。有关常用不常的划分,是国家管的事,我们就不多说 了。 我一直在网上叫“南郁”,大家可以拿你的名字和我做一下 strcmp,看看谁的名字比较大。 (友情提醒:名字大没有什么好处,相反,名字大了,在各种场合里,一般是排名靠后的……) 2 1 .5.2.2 字符串复制 char *strcpy(char *dest, const char *src); 该函数用于将字符串 src 的内容,复制给 字符串 dest。 注意,一定要保证 dest 有足够 的空间。 该函数最后返回 dest. 比如: char name1[10];