第十章 循环语句

10.1 while 循环

10.2 do ... while 循环

10.3 for 循环

   10.3.1 循环条件三要素

   10.3.2 三要素在for循环结构上体现

10.4 多层循环

10.5 小结及一点问题

 

 

循环就是反复。

生活中,需要反复的事情很多。譬如你我的整个人生,就是一个反复,反复每一天的生活,直到死,幸好,我们每天的生活并不完全一个样。

 

10.1 while 循环

 

语法形式:

 

while(条件)

{

  需要循环执行的语句; 

}

 

while 是“当”的意思。

 

请首先和if语句作一个比较:

 

if(条件)

{

  条件成立时执行的语句;

}

 

二者除了关键字不一样以外,结构完全一样。但一定要注意,在条件成立时,if语句仅仅执行一遍,而while语句则将反复执行,直到条件不再成立。

请看while循环的流程图:

 

程序从“前面的语句”开始执行,然后进行条件判断,如果条件成立,则执行一次“每次循环执行的语句”,再后请特别注意红色部分,这是我们碰上的,第一次会往后走流程:红线就像汽车拐弯,掉头到条件处(并不包括前面的语句),然后再进行下一次的条件判断……直到某一次判断时条件不成立了,程序“继续后面的语句”。

 

 

我们用while的语法套用生活中的实际例子,可以直观地看出while的用法。

假设有一个爱哭的小娃娃,有一天她要求父母给买一条小红裙,可惜父母不同意,于是她就开始一个循环:
  

while ( 父母不给买小红裙)

{
      我哭;
   }

 

这段“代码”的意思是:当“父母不给买小红裙”,那么,小女孩就一遍一遍地哭。

这就是我们和循环流程的第一个遭遇战。所举的例子看似直观:“小孩一遍遍地哭,直到父母给买裙”,但真正要用程序的语言来正确地表达出来,需要很多方面要考虑到,必竟,程序是严谨的。

首先,一个合适的判断是否继续的条件相当重要。小女孩要继续哭,仅仅“父母不给买小红裙”,这显示不符合事实,想想我们小时候,再会哭,最终也有累的时候,所以,要想继续哭,我们的条件有两个:“父母不给买小红裙”并且“我还没有哭累”。

while ( 父母不给买小红裙 && 我还没有哭累)

{
      我哭;
   }

 

其次,大多数情况下,条件需要被恰当地改变。小女孩在不停地哭,那么她如何知道父母是否买了红裙呢?所以,她不能只顾哭,还得在哭的间隙观察大人是否同意买裙。至于是否哭累,我们假设小女孩有一个疲劳度,每哭一次疲劳度加1,当疲劳度到达200时,可怜的小女孩累了……

 

while(父母不给买小红裙 && 疲劳度 < 200)

{

  我哭;

  我偷看爸妈是否同意买裙;

  疲劳度++;

}

 

例一: 用 while 语句实现求从1到100的累加和。

 

求1+2的和,我们可以写 a = 1 + 2;求1加到100,我们当然可以这样写 a = 1 + 2 + 3 + ... 100.不过这样写显然太累人了,要从1写到100啊!所以聪明如高斯的你,当然也知道这样写:a = (1+100) * 50;这确实是个在任何时候都值得称赞的,又快又简的方法,只是今天我们想让计算机累一点,老老实实地从1加到100。首先用我们先学的while式的循环。

 

请同学们打开CB,然后新建一空白的控制台程序,在main()函数体加入下面黑体部分代码。然后按F9运行。查看运行结果以加深印象。

 

//---------------------------------------------------------------------------

#include <iostream.h>

#pragma hdrstop

//---------------------------------------------------------------------------

#pragma argsused

int main(int argc, char* argv[])

{

int sum = 0; //变量sum将用于存储累加和,将它初始化为0,这很重要。

int i = 1; //i是每次要加的数,它从1开始。

 

while ( i<= 100)

{

   sum += i;

   i++;

}

 

//输出累加结果:

cout << "1到100的累加和为:" << sum << endl;

getchar();

} 

sum 初始为0,然后在每一遍的循环里,它都加上i,而,i则每次都在被加后,增加1。最终,i递增到101,超过100了,这个循环也就完成了任务。

 

运行上面程序,输出结果为:

 

1到100累加和为为:5050

 

例二:用while循环实现简单的统计功能

 

统计功能在各行业里都经常用到,比如学校学生成绩总分的统计,商店中每日销售额的统计等。下面我们实现一个学生成绩的统计。

由于成绩中包含有80.5这样的需要小数的部分,所以我们使用实数类型。

 

保存,然后关闭上面的工程,然后再新建一个控制台工程。在主函数main内加入以下黑体部分的代码:

 

//---------------------------------------------------------------------------

#include <iostream.h>

#pragma hdrstop

//---------------------------------------------------------------------------

#pragma argsused

int main(int argc, char* argv[])

{

   float sum,score;

   int num; //num 用于存储有几个成绩需要统计。

   int i;   //i 用于计数

 

   //初始化:

   sum = 0; 

   i = 1;

 

   cout << "====成绩统计程序====" << endl;

   //用户需事先输入成绩总数:

   cout << "请输入待统计的成绩个数:";

   cin >> num;

   cout << "总共需要输入"<< num << "个成绩(每个成绩后请加回车键):" << endl;

 

   while ( i <= num)

   {

      cout << "请输入第" << i << "个成绩:";

      cin >> score;
         sum += score;
         i++;
      }

      //输出统计结果:
      cout << "参加统计的成绩数目:" << num << endl;
      cout << "总分为:" << sum << endl;

      getchar();

}

//---------------------------------------------------------------------------
 

以下是运行结果,我输入4个成绩参加统计:

回车结束上面的程序。稍作休息。

 

为了更直观地了解循环流程,现在我们来跟踪这段程序中的while循环。

1、首先在循环开始处设置断点(F5 功能):

 

2、按F9运行程序,在DOS窗口提示“请输入待统计的成绩个数:”时,输入4,并回车。

 

3、程序将在一瞬间运行到第一步设置的断点所在行。即 while(...)这一行。

   此时请鼠标挪到 i 上,稍等片刻,出现提示 “i=1",同样的方法可以观察num的值。

 

可见第一遍循环时,i = 1,num = 4,条件:i <= num 显然成立,循环得以继续。

 

4、按F8,程序往下运行一行,接着再按F8,程序要求输入一个成绩,请切换到DOS窗口,随便输入一个数,并回车。

回车后,程序运行到下图中蓝底的一行:

5、之后,连续按F8,你将发现程序“回头”运行到 while(...)这一行。此时,i=2, i <= num 条件仍然成立,如果您想再跟踪一遍循环,请继续按F8,如果想结束跟踪,在断点行上再按一次F5以取消断点,然后按F9,程序恢复全速运行。

(程序往回走,回到while行)

 

10.2 do ... while 循环

 

语法形式:

 

do

{

    需要循环执行的语句; 

}

while(条件);

 

和 while循环最明显的区别,就是do...while循环中,判断是否继续循环的条件,放在后面。也就是说,就算是条件一开始就不成立,循环也要被执行一次。请比较以下两段代码,前者使用while循环,后者使用do...while循环。

 

代码段一:

int a = 0;

while( a > 0 )

{

  a--;

}

 

变量a初始值为 0,条件 a > 0 显然不成立。所以循环体内的 a--;语句未被执行。

本段代码执行后,变量a值仍为0;

 

代码段二:

int a = 0;

do 

{

  a--;

}

while( a > 0 );

 

尽管循环执行前,条件 a > 0 一样不成立,但由于程序在运行到 do...时,并不先判断条件,而是直接先运行一遍循环体内的语句:a--。于是a的值成为 -1,然后,程序才判断 a > 0 ,发现条件不成立,循环结束。

 

do..while中的条件和while循环中的条件一样是:“允许继续循环的条件”,而不是“结束循环的条件”,这和Pascal语言中的do...until正好相反,学习过Pascal(Delphi)的学员可得注意。

 

以笔者的经验,do..while循环用得并不多,大多数的循环用while...来实现会更直观。下面我们仅简单地将1到100的连加程序转换为用do...while实现:

 

int sum =0;

int i=1;

 

do

{

   sum += i;

   i++;

}

while(i<=100);

 

例三:用 do...while实现可以多次统计的程序。

 

在例二中,我们做了一个统计程序。假如一个学生有三门成绩,如语文,数学,英语要统计总分,例二的程序可以方便地使用,但如果要连续统计一个班级每个学生的这三门成绩,我们就得不断地运行例二的成绩,这显然不方便。

一个同学的三门成绩需要一层循环,要不断统计多个同学各自的成绩,就需要再套上一层循环。请看下面例子中,如何在原来的 while...循环上再加一层do...while循环。

程序的思路是:统计完一遍后,就问一句是否要继续统计新同学的成绩,如果用户输入字母Y或y,表示需要统计一下位,否则,程序结束循环。

这个程序是在例二的基础上进行功能改进,以下粗体部分为新加的代码。

 

//---------------------------------------------------------------------------

#include <iostream.h>

#pragma hdrstop

//---------------------------------------------------------------------------

#pragma argsused

int main(int argc, char* argv[])

{

   float sum,score;

   int num; //num 用于存储有几个成绩需要统计。

   int i;   //i 用于计数

 

   char c; //用来接收用户输入的字母

 

  do

   {

     //初始化:

      sum = 0; 

      i = 1;

 

      cout << "====成绩统计程序====" << endl;

      //用户需事先输入成绩总数:

      cout << "请输入待统计的成绩个数:";

      cin >> num;

      cout << "总共需要输入"<< num << "个成绩(每个成绩后请加回车键):" << endl;

 

      while ( i <= num)

      {

         cout << "请输入第" << i << "个成绩:";

         cin >> score;
            sum += score;
            i++;
         }

         //输出统计结果:
         cout << "参加统计的成绩数目:" << num << endl;
         cout << "总分为:" << sum << endl;

       

      //提问是否继续统计:  

      cout <<"是否开始新的统计?(Y/N)?";

      cin  >> c;

     }

     while( c == 'y' || c == 'Y');

}

//---------------------------------------------------------------------------

程序完成一次统计以后,会提问“是否开始新的统计”,用户输入一个字母,存到变量 c,然后程序在do...while的条件里检查c是否等于‘Y’或‘y’。如果不等于,就结束循环。

由于程序在统计之后有一个提问的时间,所以,原来的getchar()就不再需要了。

在这个例子,外层循环使用do...while是最好的选择,因为,用户运行本程序,多数情况下,他至少想统计一次。

 

最后我们来看do...while循环的流程图,请和while的流程图对比。

 

 

10.3 for 循环

for 循环里在C,C++里用得最多,也是最灵活的循环语句。要学好它,需要从已经学过的while循环的身上,“挖掘”出有关循环流程的要素,这些要素隐藏在while,或do...while的背后,但它将直接体现在for循环的结构上。

10.3.1 循环条件三要素

 

学习了两种循环,我们来挖掘一下循环流程中的“条件三要素”。

 

第一、条件一般需要进行一定的初始化操作。

 

请看我们用while循环实现1到100累加的代码:

 

int sum = 0; //变量sum将用于存储累加和,将它初始化为0,这很重要。

int i = 1;   //i是每次要加的数,它从1开始。

 

while ( i<= 100)

{

   sum += i;

   i++;

}

 

这段代码中,循环的条件是 i <= 100;因此,一开始,i肯定需要一个确定的值。前面的:

int i = 0;这一行代码,在声明变量i的同时,也为i赋了初始值:1。这样,条件 i <= 100 得以成立(因为i为1,所以 i <= 100 当然成立)。

 

第二、循环需要有结束的机会。

 

程序中最忌“死循环”。所谓的“死循环”就是指该循环条件永远为真,并且,没有另外的跳出循环的机会(后面将学到)。比如:

 

//一段死循环的例子:

while ( 2 > 1 )

{

  cout << "死循环" <<endl; 

}

 

执行这段代码,你会发现程序停不下来了。原因就是它的循环条件 2 > 1 永远为true。所以,一个最后可以变成不成立条件在大多数情况下是必需的。比如在while的那个例子:

while ( i<= 100)

条件 i <= 100 ,由于 i 在循环中被改变,所以它至少在理论上有可能造成 i <= 100 不成立。

 

第三、在循环中改变循环条件的成立因素

 

这一条和第二条互相配套。

比如这段代码:

int i=1;

while ( i<= 100)

{

   sum += i;

}

同样是一段可怕的“死循环”。因为i没有被改变的机会,其值永远为1,从而循环条件 i<=100也就永远为真。所以在循环中最后一句(下面加粗部分),不可遗忘。

while ( i<= 100)

{

   sum += i;

   i++;

}

当然,在这段程序里,i++除了起改变条件成立因素以外,同时也起上sum不断加递增的数,从而实现得到累加和。

 

说完这一些,我们来看C,C++中最灵活循环结构:for循环。

 

10.3.2 三要素在for循环结构上体现

 

for 循环的语法:

 

for(条件初始化;条件;条件改变)

{

  需要循环执行的语句; 

}

 

可见,for的结构中,不仅提供了的“条件”的位置,同时也提供了条件初始化,和条件改变的位置。这三者虽然在同一行上,但并不是依次连接地执行。

 

条件初始化的表达式首先被执行(并且只被执行一次);

然后程序检查条件是否成立,如果成立就执行循环体中的语句,否则直接结束循环。

执行完一遍循环以后,程序执行“条件改变”语句。

 

1到100整数累加的程序,改为for循环写,是最合适的了:

 

int sum = 0;

int i;

 

for( i=1; i <= 100;i++)

{

  sum += i;

}

 

程序先执行条件初始化语句:i=1;

然后立即判断条件 i <= 100 吗?显示,此时该条件成立;

于是程序执行循环体内的语句,此时只有一句: sum += i;

然后,执行改变条件因子的语句: i++; 此时,i值变为 2;

程序再次判断条件 i <= 100 ?,依然成立,于是开始第二遍循环……

 

变量 i 可以初始化条件时才临时声明:

for(int i = 1;i <= 100;i++) ……

 

for 语句的复合结构,使得程序变得简捷。比如上面的例子中,原来 while或者do...while结构中,循环体内必须两句语句,现在只需一句(即:i++这一句被移到for的特定位置上),这样,我们可以去除花括号:

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

   sum += 100;

当然,如果在其它情况下,for的循环体内仍需有多行语句时,{}仍是不可避免的。事实上,就算现在这种情况,我也建议大家使用花括号。这样可以让程序的结构看上去更清晰。

 

在本例中,如果非要讲究简捷,我们还可以将循环体内的那惟一的一行移到“条件改变”的位置:

for(int i=1; i<=100;sum += i,i++);

sum += i和i++之间用逗号分开。而在for后面的()行末,则直接跟上分号,表示for不必再执行其它的语句。

考虑到后置++的特性(在完成表达式的求值后,才进行加1操作),也可以将sum += i和i++合为一句:

for(int i=1;i<=100;sum += i++);

 

以上讲了for语句极尽合并之技巧,以求一个简捷。反过来,for语句也可以向 while或do...while语句一样拆开写:

 

int i = 1;

for(; i <= 100;)

{

  sum += i;

  i++;

}

 

看,条件初始化语句被移出for的结构,而条件改变语句则被当成一行普通语句,直接加入到循环体内。而在相应的位置上,只留下分号,用于表示空语句(请注意这一行中有2个分号,分别在 i<=100前后):

for (; i <= 100;)

如上行所示,for循环结构中的“条件初始”和“条件的改变”表达式都被省略,在这种情况下for和while或do...while循环完全一样。比如求1~100累加和:
int i=1,sum=0;

for(;i<=100;)

{

  sum += i;

  i++;

}

int i=1,sum=0;

while(i<=100)

{

  sum += i;

  i++;

}

下面分析几个实例:(用于分析的实例不提供上机的完整代码,请同学们自行创建空白工程,然后加入需要代码,确保每个实例都可运行,这是初学者逐步熟练的必经之路……信不信由你。打开CB吧)。

 

题一:用for循环在屏幕上逐行输出数字:1~200。

分析:这需要一个变量,其值从1变到200,并且每变一次新值,就用 cout 语句在屏幕上输出其值。

答案:

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

   cout << i << endl;

 

由于循环中执行的动作只有一句,所以我们省略了{}。

 

题二:6能被1、2、3、6整除,这些数称为6的因子,请循环列出36的所有因子。

分析:因子?忘了吗?求36的因子,就是求1~36中哪些整数可以整除36。我们学过 % 操作符,它用于求两数相除的余数。所以整除不整除,只要看余数是否为0即可。

答案:

 

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

{

   if(36 % i == 0)   //余数为0,说明整除

      cout << i << " "; //多输出一个空格,用于做两数之间的间隔

}

 

如果运行程序,得到结果应是:

1 2 3 4 6 9 12 18 36 

 

在这道题中,我们也看到了两种流程的结合:for循环流程和if条件分支流程。复杂问题的解决,往往就是条件流程和循环流程的种种组合,下面要讲的多层循环也是这些组合中一种。

 

10.4 多层循环

 

有些问题需要多层循环嵌套才能解决。前面可以多次统计的程序,就使用了两层循环。外层的do...while实现重复统计,内层的while循环实现一次统计。

 

继续分析一些题目:

题三:输出以下内容,要求使用两种方法,第一种方法采用单层循环,第二种方法采用双层循环。

 

123

456

789

 

方法一:

分析:单层循环的思路是:从1输出到9,并且,每当输出三个数字时,多输出一个换行符。

答案:

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

{

  cout << i;

 

  if( i % 3 == 0) //又一次用到“求余”操作。

     cout << endl;

}

 

方法二:

分析:双层循环的思路是:输出三行,每行输出三个数字。

答案:

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

{

    for(int j=i;j<=i+3;j++)

    {

        cout << j;

    }

   

    cout << endl;

}

代码中,内层的for用于输出每一行的数字,而外层的for则在每一行输出完成后,输出一个换行符,用于换行.需要另加注意的是,内层循环的条件初始化,和外层循环有关。即这一句:int j=i; 正是。每次开始内层循环时,j的值都将从当前i的值开始。

这道题似乎让人留恋于用单层循环解决一切,因为看上去用双层循环并不是很直观?

 

题四:请用输出以下内容:

 

1

12

123

1234

12345

123456

1234567

12345678

123456789

 

题目刚出,只见一同学噼噼啪啪开始输入代码,并且很快在屏幕上输出正确的内容,他的答案是:

 

cout << "1" << endl;

cout << "12" << endl;

cout << "123" << endl;

cout << "1234" << endl;

cout << "12345" << endl;

cout << "123456" << endl;

cout << "1234567" << endl;

cout << "12345678" << endl;

cout << "123456789" << endl;

 

如果谁在有关循环的作业中交上类似这样的答案,我准备给他一个“鸭蛋”呵。

这道题目,除非跟自已过不去,否则没有人会非要硬去用一层循环来实现(用一层循环不如用上面的“答案”,笨是笨点,却最为直观)。本题使用双层循环来实现实为最佳方法。

 

分析:外层循环用于控制输出9行;内层循环用于输出每行的数字。每一行都是从1开始,但第一行输出1个数字,第二行输出2个,第三行输出3个……

 

答案:

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

{

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

    {

       cout << j;

    }

 

   cout << endl;

}

 

看,在这道中,内层循环的条件初始化和外层循环无关,但循环条件判断却和外层的i有关了(j<=i)。当然,这并不是必要条件,但内层循环的条件初始化或条件判断,和外层循环的某些因素有关,这是很多多层循环的解决问题的关键!继续一个经典的题目。

 

题五:请输出以下九九口诀表:

 

1*1=1

1*2=2 2*2=4

1*3=3 2*3=6 3*3=9

1*4=4 2*4=8 3*4=12 4*4=16

1*5=5 2*5=10 3*5=15 4*5=20 5*5=25

1*6=6 2*6=12 3*6=18 4*6=24 5*6=30 6*6=36

1*7=7 2*7=14 3*7=21 4*7=28 5*7=35 6*7=42 7*7=49

1*8=8 2*8=16 3*8=24 4*8=32 5*8=40 6*8=24 7*8=56 8*8=64

1*9=9 2*9=18 3*9=27 4*9=36 5*9=45 6*9=36 7*9=63 8*9=72 9*9=81

 

分析:你可以看出,本题和题四有很大的类似,都是要输出一个“三角形”(严格说是梯形?),所以解题思路也大致一样:输出九行。你可能会说,输出九列也可以吧?的确可以,不过由于我们现在使用控制台(DOS)窗口来输出,所以被限定只能由上到下一行一行输出,而不能由左到右一列列输出。

既然是按行输出,我们来看每一行的内容有什么特点:

每一行输出的内容都是: i*j=k,其中k是积,由i和j决定,所以我们可以不必看:

 

第1行:1*1,只有一个,看不出有什么特点。

第2行:1*2 2*2  ……1,2 分别乘以2

第3行: 1*3 2*3 3*3  ……1,2,3分别乘以3

所以,各行的内容的规律是:设当前为第line行,则输出 n*line ,n为1到line.

 

for(int line=1; line <= 9;line++)

{

   for(int n=1; n<=line;line++)

   {

      cout << n << '*' << line << '=' << n * line <<' ';

   }

 

  cout << endl;

}

 

把line换成i,n换成j,本题的循环控制部分的代码和题四完全一样。

 

理解以上各分析题,最好的方法是实际上机,然后运行,并且最好自已尝试按F8来一步步运行。所以再说以遍,本章内容可以宣告结束,但如果你没有上机操作这些循环题,就不能说你学好本章。

 

10.5 小结及一点问题

 

学习了三种循环流程:while,do...while,for。

 

while在每一遍循环开始时检查条件是否成立,如果一开始条件就不成立,则循环中的语句将一次也没有执行。

 

do...while的特殊之处在于,第一遍循环前,并不检查条件,所以,就算条件根本就不成立,循环也将并执行一次,如:

do

{

  ...

}

while(false);

条件是“false”?这也是条件吗?是的,这也是条件,一个摆明了就是不成立的条件,常见的还有:while(0)。0可以看false。相反的,摆明是真的条件是:while(true)或while(1)等。

 

for 的特殊之处在于,它除了条件判断以外,还明确地留出了条件初始化,条件变化的位置。所以,有关计数的循环最适于用for来实现,请参看课程中用for实现1到100累加的实例。

至于for的各种“变体”,只须了解,以后用得熟了,再去卖弄不迟 :) 。

 

循环就象绕圈子。比如,体育课,跑1200米,跑道一圈400米,所以我们要做的事就是一边跑一边在心里计数(当然要已数,否则老师万一少计一圈,我们可就玩完了),当计数到3圈时,“循环”结束。

在这个例子,条件初始化就将计数设为0;循环的条件则是计数小于3;条件变化则是指在每跑完一圈时,心里将计数加1;至于循环的动作,自然就是跑步了……

用while实现:

 

int js = 0; //js 取意“计数”,而不是“奸商”,也不是“句神”

 

while( js < 3)

{

  跑一圈……;

  js++;

}

 

用do...while实现:

 

int js = 0;

 

do

{

  跑一圈……

  js++;

}

while( js <3);

 

用 for 实现(最适合了)

 

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

{

   跑一圈……;

}

 

如果,我在跑步时不幸由于体力不支而晕倒……怎么办?下一章见!

 

[到页首]