前言
一、什么是语句?
二、分支语句(选择结构)
2.1 if语句
2.2 switch语句
三、循环语句
3.1 while循环
3.2 break与continue语句
3.3 getchar()与putchar()
3.3.1 缓冲区
3.4 for循环
3.4.1 一些for循环的变种
3.5 do...while循环
四、goto语句
前言
本章将详细介绍分支语句(选择结构)和循环语句。
一、什么是语句?
C语言语句可分为以下五类:
- 表达式语句
- 函数调用语句
- 控制语句
- 复合语句
- 空语句
本博客将介绍控制语句。
控制语句用于控制程序的执行流程,以实现程序的各种结构方式(在C语言中有三种结构:顺序结构、选择结构、循环结构),它们又特定的语句定义符组成,C语言中有九中控制语句。
可以分为以下三类:
- 条件判断语句(分支语句):if语句、switch语句;
- 循环执行语句:do...while语句、while语句、for语句;
- 转向语句:break语句、goto语句、continue语句、return语句。
二、分支语句(选择结构)
什么是分支语句,通俗来讲就是选择。
2.1 if语句
if语句为C语言中一种最常见的分支语句,其语法如下:
语法结构:
//单分支
if(表达式)
语句;
//双分支
if(表达式)
语句1:
else
语句2;
//多分支
if(表达式1)
语句1;
else if(表达式2)
语句2;
else
语句3;
其中表达式最终只有两个值,分别为真和假,C语言中规定,0为假,非0为真,下面用代码分别演示:
int main()
{
int age = 0;
scanf("%d", &age);
//单分支
if (age < 18)
{
printf("未成年\n");
}
//双分支
if (age < 18)
{
printf("未成年\n");
}
else
{
printf("成年\n");
}
//多分支
if (age < 18)
{
printf("少年\n");
}
else if(age>=18 && age<30)
{
printf("青年\n");
}
else if (age >= 30 && age < 50)
{
printf("中年\n");
}
else if (age >= 50 && age < 80)
{
printf("老年\n");
}
else
{
printf("寿星\n");
}
return 0;
}
在C语言中,if语句中为单条语句时可以不加大括号{},多条语句必须加大括号{},但我建议不管是单条还是多条语句,都加上大括号{},养成编程好习惯。
另外,我们还要区分以下多分支语句的情况:
//区分if...if 与 if else...if的区别
int main()
{
int age = 0;
scanf("%d", &age);
//if...if多分支
if (age < 18)
{
printf("少年\n");
}
if (age >= 18 && age < 30)
{
printf("青年\n");
}
if (age >= 30 && age < 50)
{
printf("中年\n");
}
if (age >= 50 && age < 80)
{
printf("老年\n");
}
else
{
printf("寿星\n");
}
//if else if多分支
if (age < 18)
{
printf("少年\n");
}
else if (age >= 18 && age < 30)
{
printf("青年\n");
}
else if (age >= 30 && age < 50)
{
printf("中年\n");
}
else if (age >= 50 && age < 80)
{
printf("老年\n");
}
else
{
printf("寿星\n");
}
return 0;
}
多分支 | if...if | if else..if |
功能 | 提供多种情况 | 提供多种情况 |
执行方式 | 逐一执行,每条if语句都会进行判断 | 直接进入为真的语句,并不会逐一执行 |
看看下列代码:
//悬空else
int main()
{
int a = 0;
int b = 2;
if (a == 1) //1
if (b == 2) //2
printf("hehe\n");
else
printf("haha\n");
return 0;
}
你觉得打印结果是什么呢?打印结果什么都没有。为什么呢?这是因为代码风格的问题,在使用if语句时,必须要注意的一个问题是else和if的匹配问题,上述代码中的else是与第2个if匹配的,因为在C语言中规定,else总是与离得最近的if匹配,故什么都没有打印,下列为改正代码:
int main()
{
int a = 0;
int b = 2;
if (a == 1)
{
if (b == 2)
printf("hehe\n");
}
else
{
printf("haha\n");
}
return 0;
}
因此,适当的加上大括号{},能更好的理解代码,以上的else将与第1个if匹配,这样就通俗易懂了。
并且,在书写if语句时,要编写逻辑清晰的代码,列如以下代码:
//代码1
if(condition)
{
return x;
}
return y;
//代码2
if(condition)
{
return x;
}
else
{
return y;
}
//代码3
int num = 1;
if(num == 5)
{
printf("hehe\n");
}
//代码4
int num = 1;
if(5 == num)
{
printf("hehe\n");
}
上述代码中,逻辑是相同的,但代码2与代码4的风格更好,逻辑性更强,不容易出错。
当我们想用变量与常量作为表达式时,推荐用代码4形式,不容易出错:
int num = 1;
if (num = 1)
{
printf("haha\n");
}
程序不会报错,但逻辑错误
程序会报错,这样可以防止我们出错,故推荐用以下形式:
int num = 1;
if(1 == num)
{
printf("haha\n");
}
2.2 switch语句
switch适合用于处理多分支情况,常常用于有唯一取值的情况,而if语句更经常用于处理范围。
语法结构:
switch(整数表达式)
{
case 整形常量表达式:
语句;
.....
{
比如:
输入1 | 输出星期一 |
输入2 | 输出星期二 |
输入3 | 输出星期三 |
输入4 | 输出星期四 |
输入5 | 输出星期五 |
输入6 | 输出星期六 |
输入7 | 输出星期七 |
//switch语句
int main()
{
int num = 0;
scanf("%d", &num);
switch (num)
{
case 1:
printf("星期一");
break;
case 2:
printf("星期二");
break;
case 3:
printf("星期三");
break;
case 4:
printf("星期四");
break;
case 5:
printf("星期五");
break;
case 6:
printf("星期六");
break;
case 7:
printf("星期天");
break;
default:
printf("输入错误\n");
break;
}
return 0;
}
语句中的break非常重要,如果我们少加或者不加break,上述代码将会执行不同的结果:
//switch语句
int main()
{
int num = 0;
scanf("%d", &num);
switch (num)
{
case 1:
printf("星期一");
case 2:
printf("星期二");
break;
case 3:
printf("星期三");
break;
case 4:
printf("星期四");
break;
case 5:
printf("星期五");
break;
case 6:
printf("星期六");
break;
case 7:
printf("星期天");
break;
default:
printf("输入错误\n");
break;
}
return 0;
}
当我们输入1时,将会打印星期一星期二,这是因为我们在case1中没有加break,将会一直执行,直到遇到break,跳出。故我们要养成在每一个语句项加上break的情况,但面对不同情况我们也要懂得随机应变,比如以下需求:
输出工作日和周末:
//switch语句
int main()
{
int num = 0;
scanf("%d", &num);
switch (num)
{
case 1:
case 2:
case 3:
case 4:
case 5:
printf("工作日");
break;
case 6:
case 7:
printf("周末");
break;
default:
printf("输入错误\n");
break;
}
return 0;
}
很多人会疑问,switch的default语句是必须得吗?default语句是与case不匹配后执行的语句,default语句其实也不是必须的,但我们一般会加上,这是一种良好的编程习惯。
对于switch语句,我们要关注以下两个方面:
- switch(整形表达式)
- case 整形常量表达式
switch()中只能是整形,不能是其它类型,有人会问,为什么也看见可以放字符型呢?因为字符也是整形的一类。
case 后跟整形常量表达式,注意是常量,不能放变量。
三、循环语句
while循环 |
for循环 |
do...while循环 |
while循环、for循环、do...while循环有一个共同的特点:
判断部分总是比循环体多执行一次。
3.1 while循环
while循环语法结构
while(表达式)
{
循环语句;
}
while循环语句执行流程:
3.2 break与continue语句
作用 | 作用域 | |
break | 用于永久终止循环 | while、for、do...while、switch |
continue | 跳过本次循环,执行下一次循环 | while、for、do...while |
在for循环、while循环、do...while循环中,break的功能是一样的。
但对于continue,在for循环、while循环、do...while循环中根据代码情况是有一定的差异:
while循环与do...while循环情况相同
#include<stdio.h>
int main()
{
int i = 1;
while(i <= 10)
{
if(5 == i)
continue; //此时的continue将跳过本次循环,执行下一次循环,但控制条件i,在continue
//语句的后面,因此该程序将进入死循环状态
i++;
printf("%d ", i);
}
do
{
if(5 == i)
continue; //此时的continue将跳过本次循环,执行下一次循环,但控制条件i,在continue
//语句的后面,因此该程序将进入死循环状态
i++;
printf("%d ", i);
}while(i <= 10);
for(i=1; i<=100; i++)
{
if(5 == i)
continue; // 此时continue将跳过本次循环,执行下一次循环,for循环中的控制条件i是
// 不受影响的
printf("%d ", i);
}
}
3.3 getchar()与putchar()
作用 | 返回值 | |
getchar() | 从键盘读取字符 | 返回类型为int, 成功:读取到字符后,返回该字符的ASCII码值 失败:读取返回EOF --> -1 |
putchar() | 打印字符 | int类型 |
3.3.1 缓冲区
概念:我们进行从键盘输入时,并不是直接传递到了程序上,先是存放在缓冲区中。
//代码什么意思?
#include <stdio.h>
int main()
{
int ch = 0;
while ((ch = getchar()) != EOF)
putchar(ch);
return 0;
}
该代码是一种多组输入的形式,重复在键盘上进行输入然后读取打印,直到输入失败EOF结束。
想要结束:Ctrl+z,具体的原理图如下:第一次缓冲区里有A\n,putchar()读取走了A,又继续输入B\n,此时缓冲区里有\nB\n,然后又进行读取,因此可以看到打印结果是字符换行字符换行.....
因此,getchar()常常用来清空缓冲区:
int main()
{
printf("请输入密码:>");
char password[20] = { 0 };
scanf("%s", password);
//清理缓冲区
getchar()
printf("请确认密码(y/n):>");
int ch = getchar();
if (ch == 'y')
{
printf("确认成功\n");
}
else
{
printf("确认失败\n");
}
return 0;
}
这份清空缓冲区的代码是有bug的,当scanf输入:123空格abc\n 时,getchar()只读取了空格,而空格后还有东西。
scanf()读取,默认遇到空格就停止
正确写法如下:
int main()
{
printf("请输入密码:>");
char password[20] = { 0 };
scanf("%s", password);
//清理缓冲区
while (getchar() != '\n')
{
;
}
printf("请确认密码(y/n):>");
int ch = getchar();
if (ch == 'y')
{
printf("确认成功\n");
}
else
{
printf("确认失败\n");
}
return 0;
}
当然,如果想继续读取空格后的内容的话,可以参考如下代码:
int main()
{
char arr[20] = {0};
scanf("%[^\n]", arr);
printf("%s\n", arr);
return 0;
}
scanf("%[^\n]", arr);中的%[^\n]表示读取到\n停止,当然也可以将\n换成自己的需求。
3.4 for循环
for循环语法结构
for(表达式1; 表达式2; 表达式3)
{
循环语句;
}
表达式1:为初始化部分,用于初始化循环变量的。(该部分仅仅首次循环执行)
表达式2:为条件判断部分,用于判断循环什么时候终止。
表达式3:为调整部分,用于循环条件的调整。(执行完循环体后才执行该部分)
for循环的执行流程图:
对比一下for循环和while循环:
int i = 0;
//实现相同的功能,使用while
i=1;//初始化部分
while(i<=10)//判断部分
{
printf("hehe\n");
i = i+1;//调整部分
}
//实现相同的功能,使用for
for(i=1; i<=10; i++)
{
printf("hehe\n");
}
与while循环的区别在于初始化和调整部分放置位置不同,这也是for循环的优点之一,修改起来更加方便。
看一下代码:
int i = 1;
for(i = 1; i<=10; i++)
{
i = 0 // 可行吗??
}
这是不规范的代码,容易产生Bug,建议不可在for 循环体内修改循环变量,防止 for 循环失去控制。
在看以下两份代码:
int i = 0;
//前闭后开的写法
for(i=0; i<10; i++)
{}
//两边都是闭区间
for(i=0; i<=9; i++)
{}
你觉得那种定义更加直观呢?建议议for语句的循环控制变量的取值采用“前闭后开区间”写法。
前闭后开能更加直观的直到该循环执行多少次。(看判断条件就可以了)
3.4.1 一些for循环的变种
//代码1:for循环的三个部分均可以省略
//该程序是一个死循环,造成死循环的原因:省略了判断部分
//for循环中,判断部分不写,恒为真
for(;;)
{
printf("hehe\n");
}
//代码2
int i = 0;
int j = 0;
//如果省略掉初始化部分,这里打印多少个hehe?
//3个。
//for循环省略掉初始化条件部分,会造成的影响,
//因为for循环中初始化部分在整个循环中只会执行一次,
//当程序结束,又一次执行内层循环时,本该为j重新赋值时,发现没有初始化部分,此时j已自增到3了
for(; i<10; i++)
{
for(; j<10; j++)
{
printf("hehe\n");
}
}
//代码3-使用多余一个变量控制循环
//此情况根据具体需求设定
int x, y;
for (x = 0, y = 0; x<2 && y<5; ++x, y++)
{
printf("hehe\n");
}
针对代码2:
笔试题:
//请问循环要循环多少次?
#include <stdio.h>
int main()
{
int i = 0;
int k = 0;
for(i =0,k=0; k=0; i++,k++)
k++;
return 0;
}
答:0次。因为判断部分k = 0 是一种赋值语句,因此判断部分为0,则为假,不进入循环体。
3.5 do...while循环
//do...while循环语法结构
do
循环语句;
while(表达式); //注意结尾要加;
do..while语句执行流程:
do...while循环最大的特点:循环至少执行一次。
四、goto语句
//goto语句语法结构
goto label; //label可以是任何除C关键字以外的纯文本,它可以设置在C程序中goto语句的前面或者后面。
label:
根据需求编写代码....
C语言中提供了可以随意滥用的 goto语句和标记跳转的标号。 从理论上 goto语句是没有必要的,实践中没有goto语句也可以很容易的写出代码。 但是某些场合下goto语句还是用得着的,最常见的用法就是终止程序在某些深度嵌套的结构的处理过程。列如:
for(...)
for(...)
{
for(...)
{
if(disaster)
goto error;
}
}
…
error:
if(disaster)
// 处理错误情况
注意:在任何编程语言中,都不建议使用 goto 语句。因为它使得程序的控制流难以跟踪,使程序难以理解和难以修改。任何使用 goto 语句的程序可以改写成不需要使用 goto 语句的写法。
以下为一个利用goto语句编写的关机程序:
#include <stdio.h>
#include <stdlib.h> //使用system()需要包含的头文件
int main()
{
char input[10] = {0};
system("shutdown -s -t 60");
again:
printf("电脑将在1分钟内关机,如果输入:我是猪,就取消关机!\n请输入:>");
scanf("%s", input);
if(0 == strcmp(input, "我是猪"))
{
system("shutdown -a");
}
else
{
goto again;
}
return 0;
}
使用循环实现:
#include <stdio.h>
#include <stdlib.h>
int main()
{
char input[10] = {0};
system("shutdown -s -t 60");
while(1)
{
printf("电脑将在1分钟内关机,如果输入:我是猪,就取消关机!\n请输入:>");
scanf("%s", input);
if(0 == strcmp(input, "我是猪"))
{
system("shutdown -a");
break;
}
}
return 0;
}