今天是星期二,谢耳朵在控制台上输入了2.这个时候,整型变量day的值就是2.现在我们进入switch:很显然,case 2匹配上了我们的day,因此我们跳到case 2开始执行里面的语句:
case 2:
printf("汉堡\n");
break;
我们首先执行printf:在控制台上,我们输出了"汉堡",这一句完成。接下来,我们遇到了一个break.按照我们刚刚的讲解,遇到break之后我们就跳出整个switch语句,这一部分就结束了。
所以,当谢耳朵输入了2之后,整个switch语句的执行效果就是:在屏幕上打印了一句
"汉堡",然后结束。谢耳朵输入其他case的值(1,3,4,5,6)效果是类似的。
如果今天是星期天,谢耳朵输入了7,此时day的值是7,那么在switch中我们挨个遍历所有的case,发现和day的值都不符合,于是我们最后就落脚在default上,执行printf,输出一句"谢耳朵要自己做饭",然后switch这一段结束。
好,截止到目前为止,这个程序应该还是很清晰的。接下来我们来对谢耳朵的程序做一些小改动来说明break的作用:
int day;
scanf("%d", &day);
switch(day)
{
case 1:
printf("燕麦粥\n");
break;
case 2:
printf("汉堡\n");
case 3:
printf("奶油土豆汤\n");
case 4:
printf("披萨\n");
break;
case 5:
printf("法式面包\n");
break;
case 6:
printf("中餐\n");
default:
printf("谢耳朵要自己做饭\n");
}
如果谢耳朵输入了1,那么结果和之前一样--在屏幕上打印一句"燕麦粥",结束。
如果,谢耳朵输入的是2,这个时候程序的结果就不一样了--首先,我们会跳到case 2当中,在屏幕上输出"汉堡".执行完了这句printf之后,我们发现没有break,于是程序会接着跳到case 3中,执行"奶油土豆汤".接下来由于case 3中也没有break,于是程序继续向下执行,进入case,输出"披萨",终于我们这次看到了break,于是执行完了case 4之后,跳出switch,结束。所以这时如果谢耳朵输入了2,他看到的结果将是:
2
汉堡
奶油土豆汤
披萨
:动手体验:
加上main函数和必要的头文件,在你的电脑上实现上一节的程序,输入6,猜猜输出会是什么?输出和你预想的一致吗?
循环结构
介绍完了顺序和选择结构之后。我们在本节向大家介绍循环结构。顾名思义,循环结构就是一遍又一遍地做重复的事情。我们还是用开车的例子来说明:在F1比赛中,10只车队的20位车手们驾驶着各自的赛车在一条数公里的环形赛道上风驰电掣,每场比赛中选手需要环绕赛道数十圈,最先跑完全程的车手获得冠军。假设在一次比赛中,车手们需要跑完50圈,利用此前学习的顺序和选择结构,我们可以用如下方式表示这个过程:
int count = 0;
if (count < 50)
{
printf ("我跑了一圈\n");
count++;//count = 1
}
if (count < 50)
{
printf ("我跑了一圈\n");
count++;//count = 2
}
…
当然,这样手工写50遍if语句是非常非常不可取的。幸运的是,C语言的表达能力不会如此低下,循环语句就可以帮助我们简化上述过程。
在本节中,我们首先向大家介绍for循环,它特别适合循环次数已知的情况;随后我们将介绍while循环和do while循环,这两种循环都比较适合应用在循环次数未知的情况。
for循环结构
for循环有可能是最常见的循环了,我们以后会经常和它打交道。for循环的语法如下:
for (表达式1;条件判断;表达式3)
{
表达式2;
}
for循环的执行过程是这样的:
1. 首先计算表达式1;
2. 判断条件是否为真,如果为假,跳出循环不再执行;
3. 如果条件判断为真,进入循环,执行表达式2;
4. 执行表达式3,转到步骤2.
我们来看一个具体的例子:
int sum = 0;
for (int i = 1; i <= 100; i++)
{
sum += i;
}
上述循环完成了求前100个自然数和的工作。如果你还没有反应过来,没有关系,我们可以把它拆开来一步一步执行:
1. 首先,i被赋值为1;
2. 条件判断:i<=100吗?正确,继续执行;
3. 执行sum+=i,此时sum=1;
4. 执行i++,此时i=2;
5. 再次进行条件判断:i<=100正确;
6. 执行sum+=i,此时sum=1+2;
7. 执行i++,此时i=3;
等等。我们跳过中间的过程,直接观察这个循环什么时候结束:假设某一次循环执行完i++之后i=100,我们进行条件判断i<=100为真,开始执行sum+=i,这时sum里面保存了1加到100的值。接下来,执行i++,i被修改为101了,此时不满足i<=100了,因此跳出循环。最终sum中的值为前100个自然数的和。
需要强调的是,如果在表达式1中定义了新变量i,那么这个变量只在循环内是可见的,也就是说只有在这个循环内可以使用变量i,当我们跳出了这个循环之后,程序就不认识i是什么了。如果你在循环外使用i的话:
int sum = 0;
for (int i = 1; i <= 100; i++)
{
sum += i;
}
printf("%d\n", i);
你会发现编译无法通过--你的编译器不认识printf中的i是什么。当然如果你事先在循环体外部声明了i的话:
int sum = 0;
int i;
for (i = 1; i <= 100; i++)
{
sum += i;
}
printf("%d\n", i);
那printf就能够认出需要输出的i是一个int类型的变量。
如果循环体当中只有一条语句,那么循环体的花括号可以省略。在上面的例子中,我们也可以把它精简成如下形式:
int sum = 0;
for (int i = 1; i <= 100; i++)
sum += i;
当然是否省略花括号只反映了不同的编程风格,并不会影响程序的结果。读者可以根据自己的喜好选择适合自己的编程风格。
M脚下留心:为什么我的循环程序不对?
如果一切正常的话,上述循环的执行结果是5050.然而,假设有一个粗心的程序员把代码敲成了这样:
int sum = 0;
for (int i = 1; i <= 100; i++);
{
sum += i;
}
你会发现你的程序编译无法通过了。我们查看VS的报错,发现是sum+=i当中的i没有定义。这说明sum+=i根本就不在循环体内--循环究竟在哪里呢?答案是:这是一个空循环!注意我们加在for语句后面的分号事实上形成了一条什么都不干的空语句:
for (int i = 1; i <= 100; i++)
{
;
}
{
sum += i;
}
所以整个for循环空转了100次,跟在最后的sum+=i只执行了一遍,而且还不知道i是个什么值,自然编译器就报错了。
while循环结构
我们之前说过for循环特别适合已知循环次数的情况。正如同上面求前100个自然数的和,我们知道循环100次就可以完成这件事。然而,在有些情况下我们事先无法知道循环次数,这时候使用for循环就不太自然(当然,并不是说for循环不可以完成这样的工作):
:动手体验:前多少个自然数的和大于1000
我们已经学会了利用for循环求解前N个自然数的和,其中N是给定的整数。现在假设有某一个N满足前N个自然数的和大于1000,但是前N-1个自然数的和小于等于1000(你可以想象成当我们求和时,加到了N的时候,和刚刚好超过了1000)。你能用for循环实现这个程序吗?
:动手体验:猜数字
假设Foo和Bar两个人在玩猜数字的游戏。Foo在心中想了一个0到1000的整数(包括0和1000),Bar每次可以向Foo提问"你心里想的数大于XXX吗?",Foo会给出真实的回答。假设Bar每次都采用二分的策略来提问,即每次都将Foo心中的数所在区间缩小一半。举个例子:
Bar:这个数大于500吗?Foo:对 ([501, 1000])
Bar:这个数大于750吗?Foo:错([501, 750])
Bar:这个数大于625吗?Foo:对([626, 750])
等等。如果我们用C语言来模拟上述过程的话,循环的次数(Bar猜测的次数)和Foo心中一开始想的数字有关,我们根本无法事先得知循环的次数。
对于上面这些不知道循环次数的情况,C语言提供了比for更合适的while语句来解决这个问题:
while (条件判断)
{
表达式;
}
while语句看上去要比for简洁不少:首先while会进行条件判断,如果为假就跳出循环,如果为真就进入循环体执行表达式,执行完之后继续条件判断,如果为假跳出,为真则进入循环体继续执行,等等。可以看出,while循环和下面的for循环是一致的:
for (;条件判断;)
{
表达式;
}
在这个特殊的for循环中,初始化语句为空,每次循环结束的更新语句也为空。
我们用本节开头的第一个例子来展示while循环的用法:
int sum = 0;
int i = 1;
while (sum <= 1000)
{
sum += i;
i++;
}
printf("%d\n", i-1);
我们来顺着while循环的执行过程将上述程序走一遍:
1. 初始化sum和i;
2. 判断sum<=1000,正确;
3. sum+= i;此时sum=1;
4. i++;此时i=2;即下一次要加的数;
5. 判断sum<=1000,正确;
6. sum+=i,此时sum=1+2;
7. i++;此时i=3;
等等。这里跳出循环时i的值需要我们稍微推敲一下:当使得sum恰好超过1000的那个N出现时(即i=N时),此时sum保存了1到N-1的和,它是小于等于1000的,因此下一次循环开始时可以正常进入循环体。接下来sum加上了N(超过1000了),i被修改成了N+1,因此下一次条件判断就失败了,跳出循环。此时i中保存的是N+1的值,为了得到N我们需要输出i-1.
do-while循环
do-while循环是while循环的一种变体:
do
{
表达式;
}while(条件判断);
稍有不同的是,在do-while循环中,首先执行表达式,然后进行条件判断,如果为真,就回头继续执行表达式,否则就跳出。我们可以把do-while循环改写成如下的for循环:
for (表达式;条件判断;)
{
表达式;
}
do-while循环不像前两种循环那么常见,读者如果要使用do-while循环的话,请留心这一点:循环体中的表达式至少会被执行一次。
continue,break与return
在循环结构的最后,我们介绍continue,break和return,它们可以大大扩展循环结构的表达能力。
continue,break和return的直接作用都是中断当前的循环,不同的是,在此之后,continue会开始下一次循环;break会跳出循环,而return会直接跳出当前函数(我们目前的循环都是在main函数中,在main函数中执行return就相当于整个程序结束了)。我们以表达能力最丰富的for循环来举例:continue的作用如下:
for (表达式1;条件判断;表达式3)
{
表达式2-1;
continue;
表达式2-2;//不会被执行!
}