概述
本章内容包括:
*for 循环
*表达式和语句
*递增运算符和递减运算符:++和–
*组合赋值运算符
*复合语句(语句块)
*逗号运算符
*关系运算符:>、>=、==、<=、<和!=
*while循环
*typedef工具
*do while 循环
*字符输入方法get()
*文件尾条件
*嵌套循环和二维数组
计算机要做除了存储数据外的其他工作,比如对数据进行分析、合并、重组、抽取、修改、推断、合成以及其他操作。有时甚至会歪曲和破坏数据,不过我们应当尽量防止这种行为的发生。为了发挥其强大的操控能力,程序需要有执行重复的操作和进行决策的工具。当然,C++提供了这样的工具。事实上,它使用与常规C语言相同的for循环、while循环、do while循环、if语句和switch语句。
5.1 for循环
很多情况下都需要程序执行重复的任务,如将数组中的元素加起来或将歌颂生产的赞歌打印20份,C++中的for循环可以轻松的完成这些任务。
程序清单:了解for循环所做的工作
#include <iostream>
int main()
{
using namespace std;
int i;
for(i=0;i<5;i++)
cout<<"C++ knows loops.n";
cout<<"C++ knows when to stop.n";
return 0;
}
程序说明:
该循环首先将整数变量i设置为0,这是循环的初始化(loop initialization)部分。然后,循环测试(loop test)部分检查i是否小于5,如果确实小于5,则程序将执行接下来的语句——循环体,然后,程序使用循环更新(loop update)部分将i加1,i++。
这里使用了++运算符——递增运算符(increment operator),它将操作数的值+1。递增运算符并不仅限于用于for循环。例如,在程序中,可以使用i++;来替换语句i=i+1;将i加1后,便结束了循环的第一个周期。
接下来,循环开始了新的周期,将新的i值与5进行比较。由于新值也小于5,因此循环打印另一行。然后再次将i+1,从而结束这一周期。这样又进入了新的一轮测试、执行语句和更新i值。这一过程将一直进行下去,直到循环将i更新到5为止。这样,接下来的测试失败,程序将执行循环后的语句。
5.1.1 for循环的组成部分
for循环为执行重复的操作提供了循环渐进的步骤。它是如何工作的呢?for循环的组成部分完成下面这些步骤。
1.设置初始值
2.执行测试,看看循环是否应当继续进行
3.执行循环操作
4.更新用于测试的值
C++循环设计中包括了这些要素,很容易识别。初始化、测试和更新操作构成了控制部分,这些操作由括号括起。其中每部分都是一个表达式,彼此由分号隔开。控制部分后面的语句叫做循环体,只要测试表达式为true,它便被执行:
for(initialization;test-expression;update-expression)
body
C++语法将整个for看作一条语句——虽然循环体可以包含一条或多条语句。
循环只执行一次初始化。通常,程序使用该表达式将变量设置为起始值,然后用该变量计算循环周期。
test-expression(测试表达式)决定循环体是否被执行。通常,这个表达式是关系表达式,即对两个值进行比较。这个例子将i的值同5作比较,看i是否小于5,。如果结果比较为真,则程序将执行循环体。实际上,C++并没有将test-expression的值限制为只能为真或假。可以使用任意表达式,C++将把结果强制转换为bool类型。因此,值为0的表达式将被转换为bool值false,导致循环结束。如果表达式的值为非零,则被强制转换为bool值true,循环将继续进行。
程序清单:通过表达式i用作测试条件来演示了这一特点。更新部分i–与i++相似,只是每使用一次,i值就减1…
#include <iostream>
int main()
{
using namespace std;
cout<<"Enter the starting countdown value:";
int limit;
cin>>limit;
int i;
for(i = limit;i;i--) //quits when i = 0
cout<<"i = "<<i<<"n";
cout<<"Done now that i = "<<i<<"n";
return 0;
}
注意,循环在i变为0后结束。
关系表达式(如i<5)是如何得到循环终止值0的呢?在引入bool类型之前,如果关系表达式为true,则被判定为1;如果为false,则被判定为0。因此,表达式3<5的值为1,而5<5的值为0。然而,C++添加了bool类型后,关系表达式就判定为bool字面值的true和false,而不是0和1了。这种变化不会导致不兼容的问题,因为C++程序在需要整数值的地方把true和false分别转换为1和 0,而在需要bool值的地方把0转换为false,非零转换为true。
for循环是入口条件循环,这意味着在每轮循环之前,都将计算测试表达式的值,当测试表达式为false时,将不会执行循环体。例如,假设重新运行上面的程序,将起始值设为0,则由于测试条件在首次被判定为false,循环体将不会被执行。
for语句看上去有些像函数调用,因为它使用一个后面跟一对括号的名称。然而,for是C++的一个关键字,因此编译器不会将for视为一个函数,这还防止将函数命名为for。
我们使用for循环完成更多的工作。用一个循环来计算连续阶乘的值,并将这些值存储在数组中。然后,用另一个循环来显示结果。
程序清单:使用循环来计算并存储前16个阶乘。
#include <iostream>
const int ArSize = 16;
int main()
{
long long factorials[ArSize];
factorials[1] = factorials[0]=1LL;
for (int i =2;i<ArSize;i++)
factorials[i]=i*factorials[i=1];
for (int i = 0;i<ArSize;i++)
std::cout<<i<<"!="<<factorials[i]<<std::endl;
return 0;
}
通常,定义一个const值来表示数组中的元素个数是个好办法。在声明数组和引用数组长度时,可以使用const值。
程序清单:按照用户选择的步长值将循环计数递增。使用表达式i=i+by,其中by是用户选择的步长值。
#include <iostream>
int main()
{
using std::cout;
using std::cin;
using std::endl;
cout<<"Enter an integer: ";
int by;
cin>>by;
cout<<"Counting by "<<by<<"s:n";
for(int i = 0;i<100;i=i+by)
cout<<i<<endl;
return 0;
}
for循环提供了一种依次访问字符串中每个字符的方式。例如,让用户输入一个字符串,然后按相反的方向逐个字符的显示该字符串。在这个例子中,可以使用string对象,也可以使用char数组,因为它们都能够使用数组表示法来访问字符串中的字符。
程序清单:
#include <iostream>
#include <string>
int main()
{
using namespace std;
cout<<"Enter a word: ";
string word;
cin>>word;
for (int i = word.size() - 1;i >= 0;i--)
cout<<word[i];
cout<<"nBye.n";
return 0;
}
递增运算符++和递减运算符–
C++中有多个常被用在循环中的运算符。递增运算符和递减运算符这两个运算符执行两种极其常见的循环操作:将循环计数加1或减1。然而,它们还有很多特点,这两个运算符都有两种变体。前缀prefix版本位于操作数前面,如++x:后缀postfix版本位于操作数后面,如x++。两个版本对操作数的影响是一样的,但是影响的时间不同。这就像对于财产来说,清理草坪前付钱和清理草坪之后付钱的最终结果是一样的,只是支付钱的时间不同。
程序清单:演示递增运算符的这种差别
#include <iostream>
int main()
{
using namespace std;
int a = 20;
int b = 20;
cout<<"a = "<<a<<": b = "<<b<<endl;
cout<<"a++ = "<<a++<<": ++b= "<<++b<<endl;
cout<<"a = "<<a<<": b = "<<b<<endl;
return 0;
}
组合赋值运算符
C++有一种合并了加法和赋值操作的运算符,能够更简洁地完成这种任务:
i += by
+=运算符将两个操作数相加,并将结果赋给左边的操作数。这意味着左边的操作数必须能够被赋值,如变量、数组元素、结构成员或通过对指针解除引用来标识的数据:
int k = 5;
k += 3; //OK,k set to 8
int *pa=new int[10]; //pa points to pa[0]
pa[4] = 12;
pa[4] += 6; //ok,pa[4] set to 18
*(pa + 4) += 7; //ok,pa[4] set to 25
pa += 2; //ok,pa points to the former pa[2]
34 += 10; //quite wrong
复合语句(语句块):
编写C++for语句的格式比较严格,因为循环体必须是一条语句。如果要在循环体中包含多条语句,这将很不方便。C++避开了这种限制的方式,通过这种方式可以在循环体中包含任意多条语句。方法是通过用两条花括号来构造一条复合语句。即代码块,代码块由一对花括号和它们包含的语句组成,被视为一条语句,从而满足句法的要求。
程序清单:使用花括号将3条语句合并为一个代码块。循环体便能够提示用户、读取输入并进行计算。
#include <iostream>
int main()
{
using namespace std;
cout<<"The Amazing Accounto will sum and average ";
cout<<"five numbers for you.n";
cout<<"Please enter five values:n";
double number;
double sum = 0.0;
for(int i =1;i<=5;i++)
{
cout<<"Value "<<i<<": ";
cin>>number;
sum += number;
}
cout<<"Five exquisite choices indeed!";
cout<<"They sum to "<<sum<<endl;
cout<<" and average to "<<sum/5<<".n";
cout<<"The Amazing Accounto bids you adieu!.n";
return 0;
}
程序清单:在声明语句中声明一个外部语块中有的变量,在声明位置到内部语句块结束的范围之内,新变量将隐藏旧变量;然后旧变量再次可见。
#include <iostream>
int main()
{
using std::cout;
using std::endl;
int x = 20;
{
cout<<x<<endl;
int x = 100;
cout<<x<<endl;
}
cout<<x<<endl;
return 0;
}
其他语句技巧——逗号运算符
语句块允许把两条或更多的语句放到按C++句法只能放一条的地方。逗号运算符对表达式完成同样的任务,允许将两个表达式放到C++句法只允许放一个表达式的地方。例如,假设有一个,每轮都将一个变量加1,而将另一个变量减1,。在for循环控制部分的更新部分中完成这两项工作将非常方便,但循环句法只允许这里包含一个表达式。在这种情况下,可以使用逗号运算符将两个表达式合并为一个:
++j,++i //two expressions count as one for syntax purposes
逗号并不总是逗号运算符。例如,下面这个声明中的逗号将变量列表中相邻的名称分开:
int i,j; //comma is a separator here,not an operator
程序清单:使用两次逗号运算符,该程序将一个string类对象的内容反转
#include <iostream>
#include <string>
int main()
{
using namespace std;
cout<<"Enter a word: ";
string word;
cin>>word;
char temp;
int i,j;
for(j=0,i=word.size()-1;j<i;--i,++j)
{
temp=word[i];
word[i]=word[j];
word[j]=temp;
}
cout<<word<<"nDone|n";
return 0;
}
注意:赋值、比较和可能犯的错误
不要混淆等于运算符(==)与赋值运算符(=)。
musicians == 4; //comparison,musicians是否等于4,该表达式的值是true或false
musicians = 4; //assignment,该表达式将4赋给musicians
for循环的灵活设计让用户很容易出错。如果不小心遗漏了==运算符中的一个等号,则for循环的测试部分将是一个赋值表达式,而不是关系表达式,此时代码仍是有效的。这是因为可以将任何有效的C++表达式用作for循环的测试条件。非零值为true,零值为false。将4赋给musicians的表达式的值为4,因此被视为true。如果以前使用过用=判断是否相等的语言,如Pascal或BASIC,则尤其可能出现这样的错误。
警告:不要使用=来比较两个量是否相等,而要使用= =
假设要知道字符数组中的字符串是不是mate。如果word是数组名,下面的测试可能并不像我们预想的那样工作:
word=="mate"
请记住,数组名是数组的地址。同样,用引号括起来的字符串常量也是其地址。因此,上面的关系表达式不是判断两个字符串是否相同,而是查看它们是否存储在相同的地址上。两个字符串的地址是否相同呢?答案是否定的,虽然它们包含相同的字符。
由于C++将C-风格字符串视为地址,因此如果使用关系运算符来比较它们,将无法得到满意的结果。
相反,应使用C-风格字符串库中的strcmp()函数来比较。该函数接受两个字符串地址作为参数。这意味着参数可以是指针、字符串常量或字符串组名。如果两个字符串相同,该函数将返回零;如果第一个字符串按字母顺序排在第二个字符串之前,则strcmp()将返回一个负数值;如果第一个字符串按字母顺序排在第二个字符串之后,则strcmp()返回一个正数值。实际上,“按系统顺序排序”比“按字母顺序排序”更准确。这意味着字符是根据字符的系统编码来进行比较的。例如,使用ASCII码时,所有大写字母的编码都比小写字母小,所以按排列顺序,大写字母将位于小写字母之前。因此,字符串“Zoo”在字符串“aviary”之前。根据编码进行比较还意味着大写字母和小写字母是不同的,因此字符串“FOO”和字符串“foo”不同。
在有些语言中,存储在不同长度的数组中的字符串彼此不相等。但是C-风格字符串是通过结尾的空值字符定义的,而不是由其所在数组的长度定义的。这意味着两个字符串即使被存储在长度不同的数组中,也可能是相同的:
char big[80]="Daffy"; //5 letters plus
char little[6]="Daffy"; //5 letters plus
顺便说一句,虽然不能用关系运算符来比较字符串,但却可以用它们来比较字符,因为字符实际上是整型。因此下面的代码可以用来显示字母表中的字符,至少对于ASCII字符集和Unicode字符集来说是有效的:
for(ch='a';ch<='z';ch++)
cout<<ch;
程序清单:在for循环的测试条件中使用了strcmp()。该程序显示一个单词,修改其首字母,然后再次显示这个单词,这样循环往复,知道strcmp()确定该单词与字符串“mate”相同为止。注意,该程序清单包含了文件cstring,因为它提供了strcmp()的函数原型。
#include <iostream>
#include <cstring>
int main()
{
using namespace std;
char word[5]="?ate";
for(char ch='a';strcmp(word,'mate');ch++)
{
cout<<word<<endl;
word[0]=ch;
}
cout<<"After loop ends,word is "<<word<<endl;
return 0;
}
程序说明:该程序有几个有趣的地方。其中之一是测试。我们希望只要word不是mate,循环就继续进行。也就是说,我们希望只要strcmp()判断出两个字符串不同,测试就继续进行,最显而易见的测试是这样的:
strcmp(word,"mate") != 0 //strings are not the same
如果字符串不相等,则该语句的值为1,否则为零。但使用strcmp(word,“mate”)本身将如何呢?如果字符串不相等,则它的值为非零(true);如果字符串相等,则它的值为零(false)。实际上,如果字符串不同,该返回true,否则返回false。因此,可以只用这个函数,而不是整个关系表达式。这样得到的结果将相同,还可以少输入几个字符。另外,C和C++程序员传统上就是用这种方式使用strcmp()的。
检测相等或排列顺序
可以使用strcmp()来测试C-风格字符串是否相等(排列顺序)。如果str1和str2相等,则下面的表达式为true:
strcmp(str1,str2) == 0
如果str1和str2不相等,则下面两个表达式都为true:
strcmp(str1,str2)!=0
strcmp(str1,str2)
如果str1在str2的前面,则下面的表达式为true:
strcmp(str1,str2)<0
因此,根据要如何设置测试条件,strcmp()可以扮演==,!=、<和>运算符的角色
程序清单:使用string对象而不是char数组
#include <iostream>
#include <string>
int main()
{
using namespace std;
string word = "?ate";
for (char ch = 'a';word != "mate";ch++)
{
cout<<word<<endl;
word[0]=ch;
}
cout<<"After loop ends,word is "<<word<<endl;
return 0;
}
5.2 while循环
while循环是没有初始化和更新部分的for循环,它只有测试条件和循环体:
while (test condition)
body
首先,程序计算圆括号内的测试条件表达式。如果该表达式为true,则执行循环体中的语句。与for循环一样,循环体也由一条语句或两个花括号定义的语句块组成。执行完循环体后,程序返回测试条件,对它进行重新评估。如果该条件为非零,则再次执行循环体。测试和执行将一直进行下去,直到测试条件为false为止。显然,如果希望循环最终能够结束,循环体中的代码必须完成某种影响测试条件表达式的操作。例如,循环可以将测试条件中使用的变量加1或从键盘输入读取一个新值。和for循环一样,while循环也是一种入口条件循环。因此,如果测试条件一开始便为false,则程序将不会执行循环体。
程序清单:使用一个while循环。
#include <iostream>
const int ArSize = 20;
int main()
{
using namespace std;
char name[ArSize];
cout<<"Your first name,please: ";
cin>>name;
cout<<"Here is your name,verticalized and ASCIIized:n";
int i = 0;
while(name[i]!='