C 语言分支与循环的基本语法并不难,已经有大量文章进行归纳总结,我这里就不再赘述。而我在学习分支与循环的过程中,发现一些重要的细节问题。特别是对于已经习惯了其他编程语言的同学,初次接触 C 语言,如果不注意区别,就特别容易出现这些问题。这里通过五个案例,来记录一下。
案例一
关键词:if 语句,条件,等于比较运算符,赋值表达式
#include <stdio.h>
int main()
{
int x = 0;
scanf("%d", &x);
if (x = 5) // error
{
printf("haha\n");
}
return 0;
}
注意这是一个错误案例。
程序本来的目的是:让用户输入一个整数,如果等于 5,则打印输出 “haha”。但是,在 if 语句的条件中,本来应该是 x == 5
,结果写成了 x = 5
,错将等于比较运算符 ==
写成了赋值运算符 =
。这就导致:无论用户输入什么,变量 x
都会被赋值为 5,然后整个赋值表达式返回值为 5 作为 if 语句的条件。由于条件非 0,被判定为“真”,条件语句中的代码块会固定被执行。
这里的错误不仅仅来源于错将等于比较运算符 ==
写成了赋值运算符 =
,同样也要注意到在 C 语言中“赋值”不仅仅是语句,还是一个“表达式”,是有返回值的。对于习惯了一些其他编程语言的人,在那里赋值仅仅是语句,不会有返回值,初次遇到 C 语言的这个特性,可能会出现这样的问题。
案例二
关键词:switch 语句,break 语句
#include <stdio.h>
int main()
{
int day = 0;
printf("Enter the day of a week: ");
scanf("%d", &day);
switch (day)
{
case 1:
case 2:
case 3:
case 4:
case 5:
printf("It is a weekday.\n");
case 6:
case 7:
printf("It is weekend.\n");
default:
printf("invalid input\n");
}
return 0;
}
注意这是一个错误案例。
程序本来的目的是:让用户输入一周的第几天,然后通过这个数字来判断这是周内还是周末;如果输入数字超过 7,则认为是不合理的输入。但照上面这样写,实际执行的结果却是:
Enter the day of a week: 5
It is a weekday.
It is weekend.
invalid input
程序实现使用 switch 分支语句。本来只需要打印输出 “It is a weekday.”,结果把后面其他分支所有的代码都执行了。
这里需要注意的是,switch 语句中的 case <value>:
仅仅是一个标签,只代表如果匹配到这个值,程序会标签处开始执行。因此,如果我们想匹配时只执行这一小部分代码,需要在这个分支的结尾处加上 break 语句,用来跳出整个 switch 语句。位于最后的分支理论上是可以不用加 break 语句的。但在习惯上,为了减少思考成本,统一在所有分支下加上 break 语句,所以最后的 default
标签下,也加了 break 语句。最后正确的代码如下:
#include <stdio.h>
int main()
{
int day = 0;
printf("Enter the day of a week: ");
scanf("%d", &day);
switch (day)
{
case 1:
case 2:
case 3:
case 4:
case 5:
printf("It is a weekday.\n");
break;
case 6:
case 7:
printf("It is weekend.\n");
break;
default:
printf("invalid input\n");
break;
}
return 0;
}
案例三
关键词:for 循环,无限循环
#include <stdio.h>
int main()
{
for (int i = 0; i < 10; ++i)
{
if (i = 5) // error
{
printf("5\n");
}
}
return 0;
}
注意这是一个错误案例。
程序本来的目的是:从数字 0 读到数字 9,如果读到数字 5,就打印输出 5。但是,这里犯了与案例一相同的一个错误,把 if 语句条件中的等于比较运算表达式 i == 5
错写成了赋值表达式 i = 5
。所以条件必然成立,固定输出 5。同时,由于变量 i
被赋值为 5,循环体结束后 i
自增 1 值变成 6,必然满足下次继续进入循环的条件。于是,这里的 for 循环变成了无限循环。
这个案例本身没有什么实际意义,主要就是要说明 C 语言与其他语言在 for 循环上的区别。以 Python 为例,Python 的 for 循环要求指定一个可遍历的对象,该对象没有一个元素,循环就执行一次。由于可遍历对象中元素的个数是固定的,所以循环的次数几乎就是固定可预见的。Python 的 for 循环,语法和其意图是一致的。但是在 C 语言中,for 循环的本意是想实现指定次数的循环,但是 for 循环的语法却不会自动满足这一点。C 语言的 for 循环,在语法上本质仍然是 while 循环,它没有指定循环次数,而是指定了循环结束条件。只是在 while 循环上增加了一点点便利而已。
因此,在 C 语言中,我们需要格外留意 for 循环是否得到了正确的执行,是否达到了我们指定循环次数的目的。
案例四
关键词:do while 语句
#include <stdio.h>
// input: an integer
// output: number of digits of the intger
// 0 shoude be counted as having 1 digit
int main()
{
int n = 0;
int len = 0;
scanf("%d", &n);
do
{
++len;
n /= 10;
} while (n);
printf("%d\n", len);
return 0;
}
这是一个正确的案例,可放心运行。
相比 while 语句和 for 语句,do while 语句的使用的频率会少很多。这里给出的是一个比较适用 do while 语句的场合。程序的目的是:用户输入一个整数,输出这个整数的位数;对于输入的 0,也需要输出位数为 1。
案例五
关键词:continue 语句,while 循环,for 循环
在本案例中,我们对比 continue 语句在 while 循环和在 for 循环中的表现。首先我们先看 continue 语句使用在 while 循环中的一个案例:
#include <stdio.h>
int main()
{
int i = 1;
while (i <= 10)
{
if (i == 5)
{
continue;
}
printf("%d\n", i);
++i;
}
return 0;
}
注意这是一个错误案例。
变量 i
等于 1、2、3、4 时,程序运行正常,通过四次循环,分别打印输 1、2、3、4。然后,出当变量 i
等于 5 时,会执行 continue 语句。continue 语句会跳过循环体中剩余的代码,后面的 ++i
没有得到执行,变量 i
保持值为 5。在下次进入循环时,由于 i
值为 5,又会继续执行 continue 语句。于是,程序进入无限循环。
对比 continue 语句在 for 循环中:
#include <stdio.h>
int main()
{
for (int i = 1; i <= 10; ++i)
{
if (i == 5)
{
continue;
}
printf("%d\n", i);
}
return 0;
}
同样在变量 i
等于 5 时,会执行 continue 语句,跳过循环体中的剩余代码。但是,对于 for 循环,在执行下一次循环之前,会先执行 for 语句头部中的 ++i
,这个不会被跳过。变量 i
会自增,值变为 6,这样就保证程序继续执行。程序最后运行的结果是,依次打印输出 1-10,但跳过 5 没有被输出。
前面的案例四,我们说明在 C 语言中,for 循环与 while 循环的本质是相同的。而在本案例中,我们也看到 for 循环在语法上也提供了一些特性,让我们能够更容易确保循环变量的调整得到执行。