- WinCE Security...
- xdebug配置说明
- VC++ 获取文件的创建、修...
- ASP进度条
- 简单代理服务器C代码实现(S...
- 程序设计竞赛试题选(02)
- 如何在ASP程序中打印Acc...
- UTF-8和16进制区间
- ASP实用技巧:强制刷新和判...
- 运行中程序删除自己的方法
- asp提高首页性能的一个技巧
- [J2EE]J2EE 应用服务器技术
- VB变量命名规范
- C语言常见错误小结
- (摘自网络)如何在IIS中调...
第十六章 数组(一)
16.1 引子 -- “小王”成绩管理系统
小王老师参加了“编程摇篮”的学习,这一天他的系主任请他写程序。 系主任提的第一个要求是: 用户输入6个班级的各自的学生英语成绩总分,要求程序输出成绩总分和平均分。
“这简单了!”小王心想,“在前面的课程里,早就有过成绩统计的例子了嘛!改改就行。”
16.1.1 “小王成绩管理系统Ver1.0”
功能:求六个班级的成绩总分及平均分 (为了方便起见,下面的所有成绩数据都使用整型,不考虑小数) ...... int cj,zcj=0,pjcj=0; //各班成绩,总成绩,平均成绩 for(int i=0; i<6;i++) { cout << "请输入第" << i+1 << "班的分数:"; cin >> cj;
zcj += cj; //累加总成绩 }
//求平均: pjcj = zcj / 6;
cout << "总成绩:" << zcj << endl; cout << "平均成绩:" << pjcj << endl; ......
小王迅速测试了一遍,没有问题,任务胜利完成!小王的形像立刻在系主任的眼里高大起来……不过,客户的需求总是在不断进步的!系主任立即提出第二个要求: 必须加入查询功能,比如说,用户输入1,则程序输出一班的成绩,输入2,则输出二班的成绩,以此类推。
“这可不好办了!南郁老师没有教我这个啊……”小王心里很着急,“要让用户可以查询,那至少我得让程序把这6个成绩记下。”小王来回看了好几遍for循环,也没有想出如何在for循环里记下用户输入的成绩。他决定放弃了……等等!不就6个班级吗?我不用循环 总行吧,在讲循环流程的那一章里,不是举了一个最笨的办法吗?(见10.4 ) (尽管这样写程序,按南郁老师说只能得到“鸭蛋”,可以桌子那边传来系主任殷切的目光……) 一阵“噼噼叭叭”,小王删除了前面所有代码,可以实现查询的新代码如下:
16.1.2 “倒退的2.0”
功能:求六个班级的成绩总分及平均分,并可实现对各班成绩查询。
...... //定义六个变量,用于存储六个班级的成绩: int cj1,cj2,cj3,cj4,classSocre5,cj6;
//老师说下面的方法很“笨”,不过没办法了,只能这样... //(反复用 Ctrl+C,Ctrl+V 还是很方便的) cout << "请输入第1班的成绩:" << endl; cin >> cj1;
cout << "请输入第2班的成绩:" << endl; cin >> cj2;
cout << "请输入第3班的成绩:" << endl; cin >> cj3;
cout << "请输入第4班的成绩:" << endl; cin >> cj4;
cout << "请输入第5班的成绩:" << endl; cin >> cj5;
cout << "请输入第6班的成绩:" << endl; cin >> cj6;
//求总成绩和平均成绩: int zcj = (cj1+cj2+cj3+cj4+cj5+cj6); int pjcj = zcj / 6;
cout << "总成绩:" << zcj << endl; cout << "平均成绩:" << pjcj << endl;
//下面是新功能:查询:
char c;
do { cout << "请输入要查询的班级次序(1~6,0:退出)" <<endl; cin >> c;
//用switch实现对用户输入的判断及分流: switch(c) { case '1' : cout << cj1 << endl; break; case '2' : cout << cj2 << endl; break; case '3' : cout << cj3 << endl; break; case '4' : cout << cj4 << endl; break; case '5' : cout << cj5 << endl; break; case '6' : cout << cj6 << endl; break; //其它的,输出出错消息: default : cout << "您的输入有误!" << endl; } } while(c != '0') ; //用户输入‘0’,表示退出 ......
手有点酸,不过毕竟实现了查询功能。小王把写好的程序拿到系主任那里一运行,主任的眼睛透过厚厚的眼镜片闪闪发光……并且开始打听“没有弯路,编程摇篮”的网址。然后他说:“小王,你这个程序太好了!我们完全有必要把它发扬光大!你这就把程序稍作修改,扩展到整个学校的5000个学生的成绩都可以查询……” “5000个学生?我岂不要定义5000个成绩变量……”小王眼前一黑,倒在地上。
16.2 数组的定义及使用请从下面的实例中讲解,数组变量与我们以前学过的普通变量在定义和使用上有什么区别。 16.2.1 实例演示处理5000个学员的成绩,是否需要定义5000个变量? 现实生活中有大量的数据类似于此,它们有相同的类型,需要的处理的方法也一致。 为了实现对这些数据统一表达和处理,C,C++提供了“数组”这一数据结构。
数据类型 数组变量名[个数常量];
和普通变量定义的语法:数据类型 变量名;相比,主要是多了一个“[个数]”。
定义数组时,方括号内的数值指明该数组包含几个元素;使用数组时,方括号内的数值表示:使用的是第几个元素。为了直观,我假设可以用汉字来命名变量: int 抽屉[100]; 这里定义了一个“抽屉数组”,这个数组包含了100个抽屉。 而在使用时: int a = 抽屉[10]; 这里的10表示我们要使用第10个抽屉(其实是第11个,见后)。
还是来看小王老师的学生成绩管理系统吧。因为主任说要管理5000个学生成绩,那么,我们就得定义一个成绩数组,包含5000个元素。
int cj[5000];
这一行我们就相当于定义了5000个int类型的变量。这5000个变量有个统一的名字:cj。为了区分出每个变量,还需要使用“[下标]”的格式指明。下标从0开始,一直到4999。比如,第一变量是: cj[0],第二个为:cj[1]……最后一个是cj[4999]。 强调:在C,C++中,数组的下标从0开始。因此,我们定义了数组 int cj[5000];得到的是从cj[0] ~ cj[4999]的变量,而不是cj[1] ~ cj[5000]。
比如,让第100个学员的成绩为80: cj[99] = 80; 为什么是99而不是100?因为数组的下标从0开始。 再比如,让变量a等于第100个学员的成绩: int a = cj[99];
有了“数组”这个新武器,要解决前面全校成绩查询问题,一下子变得简单了。
...... //定义一个含5000个元素的数组,用于存入5000个学生的成绩: int cj[5000];
//总成绩,平均成绩: int zcj=0, pjcj;
//仍然可以用我们熟悉的循环来实现输入: for(int i=0; i<5000; i++) //i从0开始,到第49999(含)个结束。 { cout << "请输入第" << i+1 << "学员的成绩:"; cin >> cj[i]; //输入数组中第i个元素
//不断累加总成绩: zcj += cj[i]; }
//平均成绩: pjcj = zcj / 5000;
//输出: cout << "总成绩:" << zcj << endl; cout << "平均成绩:" << pjcj << endl;
//下面实现查询: int i;
do { cout << "请输入您要查询的学生次序号(1 ~ " << 5000 << "):" ; cin >> i;
if( i >= 1 && i <= 5000) { cout << cj[i-1] << endl; //问:为什么索引是 i-1,而不是 i ? } else if( i != 0) { cout << "您的输入有误!" << endl; } } while(i != 0); //用户输入数字0,表示结束。 ......
代码用了两个循环——是的,有了数组,你会发现循环流程将用得越来越多——第一个 for 循环用以输入5000个成绩。第二个do...while 循环用以查询。用户可以输入1到5000之内的数字(和现实生活习惯一致)。
下面要复习“宏”的知识。如果学校多了一个学生,那么我们就得将上面所有以斜体标出的“5000”改为5001。这是一件让人觉得无趣,且很容易出错的工作。另外,现在我们要调试这段程序,难道你真的有耐心输入5000个成绩?在调试时,我们可能希望只输入5个成绩。 这时我们可以使用“宏”。下面的代码中,先是定义了一个宏:MAX_CJ_COUNT,而后所有立即数5000都用它来表示。当我们需要改变人数时,只需更改MAX_CJ_COUNT的宏定义。
//定义一个含5000个元素的数组,用于存入5000个学生的成绩: #define MAX_CJ_COUNT 5000 //定义一个宏,方便修改为实际学生人数。 int cj[MAX_CJ_COUNT];
以下代码略。有关宏的用处,请复习:5.3.2 用宏表示常数
除非确实可以一次决定某数组的大小,否则,使用一个意义明确的宏来定义数组的大小,总是一个不错的主意。 16.2.2 个数定义必须是常量再一看眼数组定义的语法: 数据类型 数组变量名[个数常量];
注意“常量”两字,这说明,个数必须是一个可以事先决定的值,并且该值不能被改变。 比如用立即数: int arr[5000];
或者用宏: #define MAX_CJ_COUNT 5000 int arr[MAX_CJ_COUNT];
或者用常量: const int max_cj_count = 5000; int arr[max_cj_count];
就是不能用变量: int max_cj_count = 5000; int arr[max_cj_count]; // error! 不能用变量指定数组的大小。
为什么呢?因为数组占用的内存空间大小必须在程序编译时决定,并且一旦决定了,就不能再改变。所以只能用常量(不变的量)来指明数组的大小。当然,这是指在数据区或栈区分配内存(和程序有一样的全存期),如果是在堆区,则可以动态地分配数组的大小,这些我们在指针一章里讲。 有关常量和变量的区别,如果有所不清,请前面章节内容。
16.3 如何给数组中的元素赋值如果把单个变量看成是“游兵散勇”的话,那么数组对应的是“集团”。集团的“兵”就是我们前面说的数组的元素。这些“兵”不再有单独的名字,而是统一使用编号来区别,这个编号,我们称为“下标”。 在和数组打交道时,我们需要分清:是对整个数组操作,还是对数组中的单个元素进行操作。
16.3.1 在定义数组时初始化。普通变量可以在定义时同时赋初值: int a = 100; 也可以在定义以后才赋值: int a; a = 100;
对于数组变量,则只能在定义时,对整个数组赋初值:
数据类型 数组变量名[个数] = {元素1初值,元素2初值,};
即,将初值用一对 {} ( 花括号 )括起来,相邻的值之间用逗号分隔。
比如: int arr[10] = {9,8,7,6,5,4,3,2,1,0}; 上面定义一了个数组 arr,共10个元素。初始值为从9到0。即,执行上面代码以后,arr[0]值为10,arr[1]值为9……arr[9]值为0。
在初始化赋值是时,注意所给值的个数不能超过数组的大小,比如: int arr[10] = {10,9,8,7,6,5,4,3,2,1,0}; //错误!越界了 你定义了数组为10个元素,可是你却赋给它11个值,这称为数组越界:你在宾馆里预定了10间房,你却让11个人去住,多出的那个人挤在哪里呢?编译器遇上这类问题时,很明智地停了下来。
不过,你可以给它少于定义个数的初值: int arr[10] = {9,8,7,5}; //允许 你定义了数组为10个元素,但你可以给它少于10个的初始值。那些没有得到初始值的元素值是多少呢?对于全局变量来说,将被编译器初始化为0,对于局部变量而言,则是未定义的值(不可预测的值)。如果只定义数组,没有对其进行任何初始化,同样适于本情况。所有元素的初值均依本数组是全局或局部变量而定,为0或未定义值。
可以跳过一两个元素不初始化吗?如: int arr[10] = {9,,7,,6}; //跳过中间的某些元素,C:OK;C++: Error。 因为我们主要学习C++,所以认为跳过数组中某些元素的初始化赋值是错误的。 也就是说,你尽可以先预定下多个房间,然后先只派部分人去住。不过C说:允许你们跳着房号住,而C++则要求依次住下,把空房留在后面。
你还可以不指定数组元素个数,直接通过对其初始化来让编译器得到它的实际个数:
int arr[] = {9,8,7}; //元素个数为: 3 即,在未指定义大小时,则初始值的个数就是数组元素的个数。
不过,你不能既不指定数组元素个数,也不进行初始化: int arr[]; //Error, 到底是几个元素? 这也没有什么不好理解的,去几个人,就开几个房间。让宾馆老板自已去数人头,我们不必非得自已报人数——前提是,房客一个一个的都得事先现身。
16.3.2 在定义之后为元素赋值很多时候,我们无法在定义数组的同时就知道数组中各元素的值,所以,这时需要在定义以后各数组中的各个元素赋值。记住,此时只能对单个元素进行直接操作。这和普通变量不一样,下面的代码是错误的:
int arr[5]; ... arr[5] = {1,2,3,4,5}; //错,在编译器看来,arr[5]是数组arr的第6个元素。 //或 arr = {1,2,3,4,5}; //错,仍然不行。 这一点和普通变量不一样。也就是说,对数组整体的初始化,只能在定义时实行。
大都数情况,我们这样改变数组中某个元素的值:
int arr[5];
arr[0] = 95; arr[1] = 89; arr[2] = 100; ...
前面关于成绩管理的例子中,已经有过如何改变数组元素值的代码:cin >> cj[i]; 这一句将用户输入的成绩赋给数组cj中的第i个元素(i从0计起)。
两个数组可以相互赋值吗?答案也是不行:
int arr1[5] = {1,2,3,4,5}; int arr2[5];
arr2 = arr2; //不行!整个数组之间不能直接相互赋值。
你可能很不理解为什么上面的代码会出错,不过在学习后面有关指针及内存地址的更多知识以后,你会明白。现在我们只能告诉你,以我们的所学,可以方便地用一个循环来 实现将一个数组内的值全部赋值给另一个数组,这也称为数组间的拷贝。
for(int i=0;i<5;i++) { arr2[i] = arr1[i]; //正确,数组元素之间可以相互赋值。 }
当然,这样做可一定要注意:当两个数组的元素个数不一致时,不要越界访问:
int arr1[5] = {1,2,3,4,5}; int arr2[6];
for(int i = 0;i<6;i++) // i 从 0 到 5 { arr2[i] = arr1[i]; }
arr2有6个元素,而arr1只有5个。当循环执行到 i为5时,访问arr1[5]将造成越界。 (越界可以直观地理解为“越出边界“,即越出数组的边界,访问到了数组以外的内存,其内容将是未知的。)
16.4 控制台下如何输入和输出数组
输入和输出属于程序设计中,用户界面这一方面的内容。在Windows图形界面和控制台或DOS的字符界面下会完全不同。
16.4.1 输入先说输入: 以前要让用户输入个某个变量的值,很简单,比如让用户输入一个学成成绩(整型):
int cj; cin >> cj;
现在,cj是一个数组: int cj[5]; 那么要输入这5个成绩,是否可以直接写:cin >> cj; ? 你可能猜到了:不行。不信可以试试(建议:不管你信不信都请试试)。 为什么不行?原因是当 cin 碰上 cj时,它只知道 cj 是整型数组,但它不知道这个数组多大。你可能又要问,为什么cin能知道cj是一个数组,却不能知道这个数组有多大呢?这又涉及到变量与内存关系。我们先来看这个图: (图:当 cin 遭遇数组,cin只“看”到数组中的第一个元素)
数组中的各个元素总是连续地存入在内存里。所以在C,C++里,为了效率,数组变量的传递,总是只传递第一个元素的内存地址,(后面的元素自然紧跟着)。所以,当cin得到 cj这个数组时,它只看到第一个元素,它知道第一个元素的大小,却并不知道整个数组的大小,也不知道到底有多少个元素在这个数组里。这就有比你是一个窗口的售票员,你知道外面来买票的是一个队伍,但你不知这个队伍到底有多少。当然,C、C++也提供“卖团体票“的函数,但那就需要向这个函数额外传递一个数组的大小的参数。
说了这么长关于输入的问题,其实解决方法我们早于用过,来一个循环流程,一个一个输入即可:
int arr[5]; for(int i=0;i<5;i++) { cin >> arr[i]; }
16.4.2 输出再说输出。
可想而已,想一次性输出一个数组中的各元素的值,也是不可能的:
int arr[5] = {1,2,3,4,5};
cout << arr; //并不能输出arr内5个元素的内容。
你不能就此放过这个问题,我一直建议要多动手,多试试,这次就可以看出作用。当你试着在C++ Builder内写上面的代码,然后编译,你会发现编译器并没有报错。程序也可以运行。 请照着下面的代码“试试”:
#include <iostream.h> int main(int argc, char* argv[]) { int arr[5] = {1,2,3,4,5};
cout << arr; //提问:输出一个数组,结果会是什么?
cin.get(); //从本章起,不再使用getchar();改为本行 }
这运行结果截图:
输出了一个怪怪的数值。这个数值正是数组 arr 在内存中的“门牌号”——地址。并且,数组的地址其实也正是数组中第一个元素的地址。关于数组与内存地址的关系。我们在下一章详细讲解。这里要关心的正事是如何输出数组中的每个元素的值,代码如下:
int arr[5]; for(int i=0;i<5;i++) { cout << arr[i]; }
同输入一样,还是对元素操作。
16.4.3 数组输入输出练习现在请大家打开CB,新建一控制台工程。实现以下要求。
接受用户的键盘输入10个整数,然后输出这10个数,每行输出1个数。 提示: 1、别忘了文件开始位置加入本行:#include <iostream.h> 2、由于Windows操作系统的问题,使得其控制台环境下,getch()或cin.get()在前面有用户输入数值并且回车的情况下,将直接接受回车字符,导致起不到“暂停”的作用。解决方法为:使用两行: cin.get(); cin.get(); 或: getchar(); getchar(); 从本章开始,我们将改为使用cin.get()来起暂停作用。请大家注意。
16.5 数组的尺寸这里的尺寸说的是数组占用内存空间的大小,即某个数组占用了多少个字节。 以前我们说一个int型数据占用4个字节,那么一个有n个元素的整型数组占用了多少个字节?几乎可直接猜到:4 * n 个字节。而一个有n个元素的字符型或bool型数组,则应占用 1 * n 个字节,因为它们单个元素的大小均为1字节。 我们用sizeof测量某个变量占用的字节数: int a; int size = sizeof(a); size 将得到 4。
同样,我们可以这样来得到数组的尺寸: int arr[10]; int size = sizeof(arr); size 将得到 40。 你可能会问,为什么前面的 cin 不知道 arr的尺寸,而sizeof却可以得知?因为sizeof事实上是在编译阶段就进行计算。
得知数组的尺寸的一个最大的用处,就是可以在程序里通过计算得到这个数组包含几个元素。这相当于一道小学算术题: 已知某个数组占用40个字节的空间,并且知道该数组单个元素占用4个字节的空间,请问这个数组包含多少个元素? 看: int arr[] = {1,2,3,4,5,6,7,8,9,0}; //计算arr数组内有多少个元素: int count = sizeof(arr) / sizeof(arr[0]);
count 将得到10,即arr数组中元素的个数。 其中 sizeof(arr) 得到 整个数组占用的字节数,sizeof(arr[0]) 得到单个元素占用的字节数。数组的基本概念就是每一个元素的大小是并且必须一致的(你可以推想以后我们会学到内中元素可以大小不一的数据结构),为什么我们要取第1个(下标为0)元素? 看看下例:
int arr[] = {0}; int count = sizeof(arr) / sizeof(arr[0]); 明白了吗?我们或许不知道或arr有几个元素,但有一点,并且只有一点可以确定:只要是数组,那么它就至少会有一个元素。可以有 int arr[1];但不允许有:int arr[0]。
最后,对于简单的数据类型,sizeof还可以对其类型进行求值: int a; int size = sizeof(a); size 将得到4
而直接对 int 操作: int size = sizeof(int); size也将得到4.
数组没有和后者对应的做法。虽然数组也可以有不同类型:整型数组,实型(浮点型)数组,布尔型数组,字符型数组,但数组的大小不仅和它的类型有关系,而且和它含有几个元素有关系。以公式表达: 数组的大小 = 数组类型的大小 * 元素个数。 或者: sizeof(数组) = sizeof(数组类型) * 元素个数 或者: sizeof(数组)= sizeof(数组[0]) * 元素个数
16.6 字符数组字符数组具有一些特殊性,主要在三方面:1、初始化,2、输入输出、3、结束字符。大家需要注意了。 16.6.1 字符数组通常用于显示前面我们用整型表示成绩,所以数组:int cj[5000];是一个整数数组,或整型数组。 其它的数据类型,如果浮点型:float、布尔型:bool 或者字符型 char 均可以有相关的数组,如: int age[] {25,32,29,40}; float money[] = {1000.50,2000,2870.95}; bool married[] = {false,true}; char name[] = {'M','i','k','e'};
最后一行定义了一个字符数组,用来存储一个英文人名:“Mike”。相对于其它数据类型的数组而言,我们将更有需要将字符数组输出。比如上面的name,如何将“Mike”输出到电脑屏幕呢?依据上一小节的知识,应该是:
char name[] = {'M','i','k','e'}; for(int i=0; i<sizeof(name) / sizeof(char); i++) cout << name[i];
上面代码的屏幕输出是: Mike
假设有一个整型数组: int age[] {25,32,29,40}; 当我们要输出它时,大抵应是希望输出如: 25 32 29 40 或者加上逗号: 25,32,29,40 或者干脆换行: 25 32 29 40
但对于字符串数组,我们更多地希望它如上面“Mike”一样连续输出。C,C++ 设计者们看到了这一点,所以针对字符数组,有以下特殊做法。说是特殊,是指其它类型的数组没有,对于人的习惯来说,它倒是非常“自然”的做法。
16.6.2 字符数组初始化首先是初始化部分,字符数组允许这样实现:
char name[] = "Mike";
对于中国人来说,这是一个“救命”的做法,我们不用去“拆”汉字了——还记得吗?一个汉字占两个字节,即一个汉字其实是由两个字符组成的。
char myname[] = "南郁";
当然,明确指定元素个数也不违法:
char name[5] = "Mike";
为什么我指定 name 的元素个数为5? 这是一件更重要的问题。后面会说到。
另外,如果你习惯于给数组加花括号,也可以:
char name[] = {"Mike"}; 当以后讲到二维数组,花括号就是必须的了。
16.6.3 字符数组的输入然后是输入:
char name[4]; cout << "请输入您的姓名,不要超过4个英文字符或2个汉字!" << endl; cin >> name;
这是运行的结果:
每一个(正直 && 认真学习)的学员都会指出老师的自相矛盾:前面刚刚说,当“cin 遭遇数组,cin只‘看’到数组中的第一个元素”,即:cin 不知道 name 的大小! 事实上 cin 仍然不知道 name 的大小。所以上段代码一开始就“请求”用户只输入4个以内的字符。用户当然不会那么听话!如果他输入5个,或更多字符,会发生什么糟糕的事情?如果你试试,你可能会发现一切正常,但其实性质严重的错误确实存在。就像一个小孩偷了家里的东西,他倒不会因此被派出所抓走(因为大多数家庭会选择内部处理),但错误的严重性值每个父母重视。
有没有办法让 cin 指接受指定个数的字符?而不管具有“破坏欲望”的用户输入多长?
(本来, cin, cout 这一类仅在 DOS或控制台下或UNIX或Linux下的字符界面下才能用的东东,都不是我们的学习目标。记住,我们只是为了学习C,C++基础知识上的方便,才染指。在讲如何实现前述要求之前,我们来插一个“广告”吧 :)放松放松。 想像南郁老师是电视里的那个漂亮的幼儿园大班女阿姨…… 大家——想像时光倒流,回到无忧无虑的童年时代,脸上露出天真无邪的笑容——为了配合场景,还得想像人手一张花花绿绿的盗版Windows XP 安装盘,用左手拿,举着,摆好POS。 好了,本阿姨问:“我们的目标是~~~”。 大家齐声回答:“Windows 编程!!”
呵呵,回答“没有弯路”也许更像一点。 Windows编程的课程即将出笼。。。一开始将都是免费的,暂不收费。但是若要为了“没有弯路“,《白话 C++ 》一定要学完学好。所以你若没有C++基础,建议不要放弃而跑到去学习《白话 Windows编程》。)
(下面这一段是选学内容) cin.getline的用法:
getline 是一个函数,它可以接受用户的输入的字符,直到已达指定个数,或者用户输入了特定的字符。 它的函数声明形式(函数原型)如下:
istream& getline(char line[], int size, char endchar = '\n');
不用管它的返回类型,来关心它的三个参数: char line[]: 就是一个字符数组,用户输入的内容将存入在该数组内。 int size : 最多接受几个字符?用户超过size的输入都将不被接受。 char endchar :当用户输入endchar指定的字符时,自动结束。默认是回车符。
结合后两个参数,getline可以方便地实现: 用户最多输入指定个数的字符,如果超过,则仅指定个数的前面字符有效,如果没有超过,则用户可以通过回车来结束输入。
char name[4]; cin.getline(name,4,'\n'); 由于 endchar 默认已经是 '\n',所以后面那行也可以写成: cin.getline(name,4);
如果你想输入三个汉字,把 name 改成6即可。作为配合,你可以用一个宏来指定name的元素个数,也可以用前面的sizeof的方法:
char name[6]; cin.getine(name,sizeof(name));
16.6.4 字符数组的输出和输入对应,字符数组也允许不用循环,直接用 cout 输出。
char name[] = "Mike"; cout << name;
上面代码的屏幕输出是: Mike 。效果和前面使用 for循环的代码完全一样。
同样的问题是,cout 怎么会知道 name 内有四个字符呢? 为了加深你对这个问题的理解,先来看下面的程序:
char name[] = {'M','i','k','e'}; //我们用“老”的方法初始 cout << name;
运行结果是:
或许这样的结果还是不足于让你“感动”。下面这个例子绝对值得你去一试。我写完整一点:
#include <iostream.h>
char name[] = {'M','i','k','e'}; char othername[] = "Tom";
int main(int argc, char* argv[]) { cout << name << endl; cin.get();
return 0; } 运行结果是:
在内存里,由于数组:othername 紧挨着 name 后面,而cout 在输出name时,它并不知道 name中有几个元素,所以把othername 的内容也当成是name的,一并输出了。这应该是一个明明白白的错误表现了。 注意,如果你想把name和othername的定义放在 main 函数内,那为了达到上面的效果,你应把二者的次序倒过来:
int main(int argc, char* argv[]) { char othername[] = "Tom"; //改成它先定义。 char name[] = {'M','i','k','e'};
cout << name << endl; cin.get();
return 0; } 这是因为函数内的局部变量是建立在栈内存,而栈内存是“倒立”的。
如何让 cout 知道 它要输出的字符数组有几个字符?并不是依靠 cout 提供对应于 getline()的函数,而是由 C++ 内建机制,提供一个间接方法来保证程序有办法断定一个字符数组的结束。这就是:为字符数组添加一个空字符为结束。 先来看答案:
char name[] = "Mike"; cout << name;
就是这么简单,奥妙在于初始化 name 时,请采用这种“特殊”而又“自然”的方法:直接给出用双引号括起来的字符串。当你用这种方法初始时,编译器在编译过程中,将偷偷地为在这个字符数组后面加上一个元素。这个元素就是空字符。 (什么叫空字符?也称“零字符”就是值为0的字符,表示为:'\0' 如果忘了,请复习第五章。)
只有使用 "Mike" 这种方法,编译器才会做这件附加的工作。采用 {'M','i','k','e'}这种方法时,空字符不会被添加。
添加了空字符,cout 就能知道 name 有多长吗?其实它仍然不知,但它不管,它只需一个一个字符地输出,直到碰上空字符,即:'\0'时,它就认为这个字符串结束了。
在 char name[] = "Mike"; 中,编译器真的往 name 后加一个空字符了吗?
我们用两个办法来验证。
第一、通过调试工具观察
眼见为实。字符数组最后的空字符仅用于表示当前字符串结束了,本身并不输出(即使输出,也是一个不占位置的字符,看不到)。但是当程序运行时,它在内存是的的确确占了一个字节,我们可以通过调试来查看。
新建一个控制台工程,添加以下黑体部分代码: #include <iostream.h> ... int main(int argc, char* argv[]) { char name1[] = {'M','i','k','e'}; char name2[] = "Mike";
cout << name1 << endl; cout << name2 << endl;
return 0; } 在其中的某行 cout << ... 上设置断点,然后运行,程序停在断点上,如下图:
将输入鼠标分别在移到代码中的 name1和name2 上,按Ctrl键,并用鼠标点击,将出现Debug Inspector窗口(调试查看器)。 可以清楚看到,name2 多了一个元素,并且值正是: '\0'。它就是编译器自动加入字符串结束标起:空字符。
第二、既然加了一个字符,那么,name[] 的大小就应该是5,而非4。为了加深记忆,我们用程序验证。
把上面的相关代码做点改动:
char name1[] = {'M','i','k','e'}; char name2[] = "Mike";
cout << sizeof(name1) << endl; cout << sizeof(name2) << endl;
结果: 4 5
正如我们所想,第一种初始化方法,会让 name1的尺寸多出一个字节,那个字节用于存入空字符。 由于引出一个重要的注意事项:指定字符数组的大小时,请注意为空字符预留一个字节。
下面是一个反例: char name[4] = "Mike"; cout << name; 由于你强制限定name为4个元素,所以编译器爱莫能助,无法为它加上空字符作为结尾。当cout仍然无法正确地输出。
正确的是: char name[5] = "Mike"; 最不会犯错的不写: char name[] = "Mike";
反过来,当采用 {'M','i','k','e'} 的形式初始化时,我们可以手工添加一个空字符: char name [] = {'M','i','k','e','\0'}; cout << name;
由于有了'\0'作为结束,所以cout可以正确地输出name。
请回答,下面的代码运行后输出应为? char name [] = {'M','i','k','\0','e'}; cout << name;
16.7 数组应用实例
我们将举几个例子,例子从简单到复杂。要求大家每个都照课程做一遍。我们不给出源代码。 以下例子均需要包含 #include <iostream.h>。若另需其它头文件,我们在例子中指出。
16.7.1 “小王成绩管理系统新版”
请自定义一个成绩数组(这回使用float类型),并求出其总分和平均分
//为了不重复太多内容,我这里直接用一个现成的数组 //你可以把它改回通过用户输入的方法得到成绩。 float cj[] = {85.5,90,100,70,60,45.5,89};
float pjcj,zcj = 0;
//成绩个数: int cjCount = sizeof(cj) / sizeof(cj[0]);
for(int i=0;i< cjCount; i++) { zcj += cj[i]; }
pjcj = zcj / cjCount;
cout << "总分:" << zcj << endl; cout << "平均:" << pjcj << endl;
cin.get();
在作业中,我们要求大家继续完善本例,让它可以变得专业: 1、可以根据平均分,评判本次试卷的出题质量。(每年教委内部都要对高考试卷进行类似的评析) 2、学员输入自已的座号,可以输出他的成线和计算机对他的评价。 3、可以求最高分和最低分。
16.7.2 “!dnalroB evol I”
请自定义一字符数组,先将其输出,然后逆序输出(不包括空字符)
char str[] = "I love Borland!";
//正常输出: cout << str << endl;
//字符串长度: int len = sizeof(str) - 1; //问:为什么要减1?
//逆序输出: for(int i=len-1; i>=0; i--) //问:为什么 i 从 len-1开始? { cout << str[i]; }
这是输出结果:
请在完成本例后,继续尝试这一要求: 如果要求使用这样的for循环: for(int i=0; i<len; i++) { //????? } 仍然要实现逆序输出,请问循环体的那行代码该如何写?这也是我们本章一道作业。
16.7.3 “数组中的玄机”这个有点意思!
有一整型数组: int flags [] = { 0,0,0,0,1,1,3,4,6,5,3,3,5,5,6,4,3,2,2,-1, 0,0,0,0,0,2,0,0,0,6,0,0,0,0,6,0,0,1,-1, 0,0,0,0,7,0,0,0,1,10,0,13,0,10,2,0,0,0,8,-1, 0,0,0,0,8,0,0,0,0,1,0,0,0,2,0,0,0,0,7,-1, 0,0,0,0,13,4,6,9,9,13,4,14,4,13,9,9,6,4,13,-1, 0,0,7,3,11,12,4,12,4,12,96,9,12,4,12,4,12,11,3,8,-1, 0,0,8,13,2,2,0,0,0,0,4,12,4,0,0,0,0,1,1,13,7,-1, 0,0,0,0,12,9,0,0,15,0,15,0,15,0,0,0,12,-1, 0,0,0,0,4,12,9,0,0,16,17,18,19,0,9,12,4,-1, 0,0,0,0,7,0,4,12,9,0,0,0,0,0,0,9,12,4,0,8,-1, 0,0,0,13,8,0,0,8,4,12,9,0,9,12,4,7,0,0,7,13,-1, 0,0,1,1,1,13,13,13,2,0,4,12,4,0,1,13,13,13,2,-1, }; 请根据该数组中各元素的值依次做如下输出:
0 => 空格 1 => ( 2 => ) 3 => ` 在数字键 1 的左边 4 => ' 单引号 5 => " 双引号 6 => - 连字符 7 => / 8 => \ 9 => . 10 => o 小写字母 o 11 => ; 分号 12 => # 13 => _ 下划线 14 => = 15 => ☆、16 => 我、17 => 爱、18 => 你 19 => !
注意除了 15,16,17,18 为汉字以外,其它均为英文状态下输入字符。而当输出汉字时,记得使用 "",因为一个汉字相当于个字符,所以应算字符串。另外:单引号、双引号、反斜杠 需要使用转义字符,详见下面代码。
看看输出结果是什么?其实这是我们小时候的游戏啦。一张看似乱七八糟的图,旁边写着:“请小朋友把图中带点的小格子涂上红颜色,看看图中隐藏着什么?”——呵呵,童年真好。快打开CB,让我们一起回到童年。
这是代码:
int flags [] = { /* 请: 从上面拷贝数组元素的值*/ }; [wangjy17908]
添加时间:2008-04-12
版权所有(C)2005-2015 |