C语言复习(2)——语句
语句复习
2.1 空语句
;
C中最简单的语句,本身只包含一个分号。空语句不执行任何任务,但仍有作用。适用的场合便是语法要求出现一条完整语句,但并不需要它执行任何任务。
2.2 表达式语句
C不存在专门的“赋值语句”,赋值就是一种操作,就像加法和减法一样,赋值利用“=”运算符在表达式中进行。在表达式尾部加上分号,就转化为语句,下面的表达式
x=y+3;
ch=getchar();
实际上是表达式语句,而非赋值语句。理解这点非常重要,因为下面的语句也是合法的:
y+3;
getchar();
当他们被执行时,表达式被求值,但是结果不保存在任何地方。因此,第一条语句并不具备任何效果,,而第二条语句则读取输入中的下一个字符接着将其丢弃(注:可以用于清空输入缓冲区
)。
如果你觉得编写一条没有任何效果的语句有些奇怪,可以考虑以下语句:
printf("Hello world!\n");
printf是一个函数,函数将返回值,但printf函数的返回值(实际打印的字符数)我们并不关心,所以弃之不理。所谓的“没有效果”
只是表达式的值被忽略,但printf所执行的打印是有用的工作,称之为“side effect”。
同理a++;
这条语句没有赋值操作,但确是一条合理的表达式。++操作符增加了a的值,这就是该语句的“side effect”。
2.3 if语句
语法如下:
if(expression)
{
statement1
}
else
{
statement2
}
注:请在任何时候都为if和else后的语句添加花括号
如果expression为真(非零
),执行statement1,否则跳过他。如果存在else子句,它后面的statement2只有当expression的值为假(零
)时才会执行。
注意以下代码:
int a=0;
if(0==(a=0))
{
printf("hehe\n");
}
else
{
printf("haha\n");
}
赋值运算的返回值就是所赋的值,故a=0
的返回值是0,此时0==0
所以if中的表达式返回值是1。
“悬空的else” 问题
先看代码:
int a = 0;
int b = 2;
if(a == 1)
if(b == 2)
printf("hehe\n");
else
printf("haha\n");
你认为else是哪个if的子句呢?这里故意用奇怪的缩进,让你有所迷惑,你是否认为输出的是“haha”
?
答案是else子句从属于最靠近他的不完整的if语句,所以这个代码块什么都不输出。如果想让它从属于第一个if,就要把第二个if补充完整,加上一条空的else子句,或者直接用花括号。
花括号,永远的神!
if (a == 1)
{
if (b == 2)
printf("hehe\n");
}
else
printf("haha\n");
输出“haha”
。
2.4 while 语句
语法如下:
while(expression)
{
statement1
}
statement2
while的流程:
expression != 0 ,执行循环体 statement1 。
expression == 0,循环结束,跳出循环执行 statement2 。
对expression的测试在statement1(循环体)之前进行,如果为假(expression==0),循环体一次也不执行。
2.4.1 break 和 continue
先上图:
以代码举例:
int ch;
while((ch=getchar())!=EOF)
{
putchar(ch);
}
//标准输入循环复制字符到标准输出,直到找到文件尾结束标识。
putchar,getchar函数。
如果循环体中执行了continue语句,循环体内的部分将不再执行,而是立即开始下一轮循环。如果循环体遇到某些值才会执行的情况下,Continue语句会相当好用。
int ch;
while((ch=getchar())!=EOF)
{
if(ch<'0' || ch>'9')
continue;
putchar(ch);
}
//只打印数字字符
注意continue不要放在循环步进之前,不然容易造成死循环,错例如下:
int i=1;
while(i<=10)
{
if(5==i)
continue;
printf("%d ",i);
i++;
}
while将在i==5后无限continue,因为始终跳过了i++
。
如果把i++放在continue之前就能避免错误。
int i=0;
while(i<10)
{
i++;
if(5==i)
continue;
printf("%d ",i);
}
如果循环体中加入break语句,循环就将永久性退出。
int ch;
while((ch=getchar())!=EOF)
{
if(ch<'0' || ch>'9')
break;
putchar(ch);
}
//遇到数字以外字符停止循环输入
提示:while语句有时在表达式中就可以完成整个语句的任务,于是循环体就无事可做,这种情况下循环体就用空语句来表示。单独用一行来表示一条空语句是比较好的做法,不至于让人误以为程序接下来的一条语句才是循环体。
while((ch=getchar())!='\n')
;
这条代码的作用是丢弃当前输入行的剩余字符,用处不小。
具体输入缓冲区可参考博客C语言中的输入输出流和缓冲区(重点)详解。
2.5 for语句
语法如下:
for(exp1;exp2;exp3)
{
statement
}
statement为循环体。
exp1为初始化,在循环开始时执行一次。
exp2为条件部分,在循环体每次执行前都要执行一次,与while语句中的expression一样。
exp3为调整部分,循环体statement每次执行完毕后执行exp3,紧接着执行exp2。
所有三个表达式都是可选的,如果省略条件部分exp2,表示测试的值始终为真;
在for语句中使用break语句,则立刻退出循环。而continue语句则把控制流直接转移到调整部分exp3。
将for语句转化为while语句:
exp1;
while(exp2)
{
statement
exp3;
}
for和while的区别在于出现continue语句时。for语句的continue跳过循环体,直接转到调整部分。在while语句中,调整部分是循环体的一部分,所以continue可能会把调整部分也跳过,直接去向条件部分。
for循环流程图如下:
查看while流程图。
2.6 do while 语句
类似while语句,只是条件部分在循环体执行之后才进行。所以这种循环的循环体至少要执行一次。语法如下:
do
{
statement
}while(expression)
当需要循环体至少执行一次时,选择do while语句
2.7 switch 语句
switch(expression)
{
case constant-expression1:
statement1;
case constant-expression2:
statement2;
....
}
每个case必须具有一个唯一的值。expression的结果必须是整型值
,当expression的返回值与常量表达式(constant-expression)匹配,即确定了语句进入点。
具体流程:
- 计算expression值
- 进入语句列表中case标签值与expression的值匹配的语句。
- 从这条语句开始,直到语句列表的结束,也就是switch语句的底部,之间的所有语句都将被执行。
注意:case的标签值只是确定了语句的进入点,但是不能划分语句列表。一旦进入语句后,就将贯穿后续的case语句,而不是停留在单个的case标签。如果想要在某个case中执行完语句后跳出switch语句块——使用break语句。
2.7.1 switch 中的 break 语句
在某个case的statement执行后添加break,便会直接跳到语句列表的末尾。
int main()
{
int i;
scanf("%d", &i);
switch (i)
{
case 1:
printf("周一\n");
break;
case 2:
printf("周二\n");
break;
case 3:
printf("周三\n");
break;
case 4:
printf("周四\n");
break;
case 5:
printf("周五\n");
break;
case 6:
printf("周六\n");
break;
case 7:
printf("周日\n");
break;
}
return 0;
}
break语句的实际效果是把语句列表分为不同部分,你会发现最后一个case的语句后面的break貌似没什么用,但是我们放在那里也没什么害处,之所以加在那里是为了以后维护方便,万一以后多了一个“周八”呢。
switch并非循环语句,continue没有任何作用。
为了使同一个statement在多个case标签之下都能运行,可以活用break,代码如下:
int main()
{
int i;
scanf("%d", &i);
switch (i)
{
case 1:
case 2:
case 3:
case 4:
case 5:
printf("工作日\n");
break;
case 6:
case 7:
printf("休息日\n");
break;
}
return 0;
}
2.7.2 default 子句
此时你可能会有个疑问,如果case的标签值没有一个与expression的结果匹配,会发生什么情况?答案就是所有的case对应语句都被跳过,程序继续往下进行且不会报错。
但是你不想让辛辛苦苦写的switch语句块就这么被白白跳过了,即使没有用到case中的语句,好歹也得知会你一声吧。
你可以在语句列表中增加一条default子句。
default :
statement
break;
当不匹配所有的case值时,这个default后面的statement便会执行。还是上述代码,添加default后,可以知道输入错误。
int main()
{
int i;
scanf("%d", &i);
switch (i)
{
case 1:
case 2:
case 3:
case 4:
case 5:
printf("工作日\n");
break;
case 6:
case 7:
printf("休息日\n");
break;
default:
printf("Wrong day\n");
break;
}
return 0;
}
default 只能出现一次,可以放在语句列表的任一位置,建议放在末尾且带上break,因为语句流会像贯穿其他case一样贯穿default。否则如下代码,default放在前面又忘记break。
int main()
{
int i;
scanf("%d", &i);
switch (i)
{
default:
printf("Wrong day\n");
case 1:
case 2:
case 3:
case 4:
case 5:
printf("工作日\n");
break;
case 6:
case 7:
printf("休息日\n");
break;
}
return 0;
}
注意
:绝大部分情况下,每个case的语句后都会带上break,如果你需要贯穿case标签,请务必注释,如果没有注释,一个粗心的维护程序员便有可能认为此处缺少break语句是个错误,是bug的起源,于是当他“修正”这个“bug”时,不仅错过了他原先寻找的bug,还引入了新bug。
2.8 总结
- if语句根据条件执行语句
- while(exp)
exp!=0 执行循环
exp==0 跳出while - for与while类似,但是便利在与把控制循环的表达式收集起来放在一个地方
- do while 语句保证循环体至少执行一次
- 当循环内部执行break语句时,循环就会退出。
- continue语句会跳过循环体的剩余部分,立即开始下一次循环。在while和do while语句中,会跳到判断循环是否要进行的条件部分,而for语句中则是先跳到调整部分(exp3),再进入判断循环是否要进行的条件部分(exp2)
- goto语句应尽量避免使用
青山不改 绿水长流