- C语言是一种结构化的程序设计语言
- 如下图所示,有三种结构,而世上几乎所有事情都可以用这三种结构表达:
- 其中,顺序结构不必多说,就是自上而下的运行程序
- 而选择结构就是C语言中的分支语句,包含if语句和switch语句
- 循环结构则就是C语言中的循环语句,包含while、for、do while这三种语句
- 那么,相信初入C语言大门的你一定很迷茫,什么是语句?语句有什么用呢?接下来我来简单的给你解释一下:
1、语句
语句可以理解为是编写代码所需的最小基本单位,无论是多大的项目都得由一行一行的语句堆砌起来,像是建房子用的砖头一样,虽然并不罕见但却必不可少。
而语句主要分为以下五类:
- 表达式语句
- eg:
1+1;
像这种是一个简单的表达式的语句,就是表达式语句
- eg:
- 函数调用语句
- eg:
printf("%d\n",a);
像这种调用printf函数的语句,就是函数调用语句
- eg:
- 控制语句
- 负责控制整个程序的运行,依靠语句实现各种上文提到的结构,此种语句为本文重点,将在下文展开讲解
- 复合语句
- 如果是一个大括号括起来,而里面有很多语句,这一整个大括号就是符合语句,里面实现了很多功能,但只是一个语句。
- 空语句
- 什么内容也没有,只有一个分号“;”
C语言中,共有9种控制语句:
- 分支语句(实现选择结构):if;switch
- 循环执行语句(实现循环结构):do while;while;for;
- 转向语句(不具体实现某个结构,但起到了操控程序的作用):break;goto;continue;return
2、分支语句(选择结构)
2.1 if 语句
if的语法结构:
if(exp)
语句;
exp是表达式的意思
那么我们就可以写一个代码来对上图的流程图进行一个实现:
#include <stdio.h>
int main()
{
int a = 0;
scanf("%d", &a);
if (a == 1)
printf("考进清北\n");
else if ( a==0 )
printf("进入大专\n");
return 0;
}
在这个代码中,输入0表示不务正业,输入1表示好好学习
不难发现,else if表示其他的选择,也可以直接用else直接表示其他所有的选择情况
- 代码运行结果如下:
- if和else后默认只能跟一条语句,如果想执行多条语句的话需要用大括号括起来变成复合语句,也叫做代码块。
- 在C语言中,0表示假,非0表示真(注意不是1表示真,是非0的任何数),所以if后的判断就是判断是否为0,如果是0就不执行,如果非0就执行。
2.1.1悬空else
上文已经提到else语句,接下来我们更深入的学习一下else的坑:
思考:下面这段代码的结果是什么?
#include <stdio.h>
int main()
{
int a = 0;
int b = 2;
if (a == 5)
if (b == 2)
printf("选择1");
else
printf("选择2");
return 0;
}
聪明的你觉得是什么结果呢?选择1?还是选择2?
答案:都不是,什么都不打印。
运行图:
为什么会出现这种情况呢?因为else语句是“就近原则”,和最近的一个if进行匹配。显然,这个代码的else和第二个if进行了匹配,而不是第一个if
所以这种“悬空”的else,并不是和谁对齐,就和谁匹配。这也提醒我们,平常的代码风格需要注意缩进和空格,让代码阅读者不要产生歧义和误会,就是最好的。
2.1.2 if 语句书写形式的对比
思考:下面两种代码风格,哪个更好?
//代码1
if (condition) {
return x;
}
return y;
//代码2
if (condition)
{
return x;
}
else
{
return y;
}
虽然两段代码的功能是一样的,但很明显,第二段代码更加清晰易懂。
思考:下面两种风格哪个更好?
//代码3
int num = 1;
if(num == 5)
{
printf("lulu\n");
}
//代码4
int num = 1;
if(5 == num)
{
printf("lulu\n");
}
这个一眼可能看不出孰优孰劣,但其中最明显的区别在于if后的条件,一个是num == 5 ,一个是5 == num,那么这两个究竟哪个好呢?
举个例子,你就明白了:
#include <stdio.h>
int main()
{
int a = 0;
if (a = 5)
printf("5\n");
return 0;
}
快,想一下这个代码的结果是什么?是5还是不打印?
- 答案是:
为什么会这样?因为条件那里不小心,少写了一个=号,从判断相等的条件变成了赋值,那么if后的条件自然变成了5,而5又是一个非0的数,非0即为真,所以if里的程序运行了。
那么如何避免这种不小心呢?我们可以从代码习惯入手:
把之前的a=5调换位置变成5=a,可以看到:
报错了
也就是说,如果你把数字和变量调换顺序的话,如果你少打了=号,程序根本运行不起来,从根本上解决了未来可能的bug的出现(因为这样很好发现问题)
那么我们再看代码4:
//代码4
int num = 1;
if(5 == num)
{
printf("lulu\n");
}
显然,是数字在前,变量在后的模式,与代码3虽然功能一样,但却减少了出错的概率以及调试的时间成本,不可谓不好。
2.1.3小练习
判断一个数是否为奇数(难度:⭐)
建议你先去编译器自己动手做一下,再继续看本文的教程
解析分割线
第一题思路:将输入的数字取模2,若余数为0则为偶数,余数为1为奇数
用if语句进行编写:
#include <stdio.h>
int main()
{
int a = 0;
scanf("%d", &a);
if (a % 2)
printf("是奇数");
else
printf("不是奇数");
return 0;
}
这里因为余数是0即为假,非0即为真,所以直接写到if的条件里即可,你也可以用它的结果和0去比较是否相等,都可以。
2.2 switch 语句
switch语句是一种多分支的语句,与if……else if……else if功能上是一样的,但是他更为简洁,也更方便阅读。
switch的语法结构:
switch(整型表达式)
{
语句项:
}
又出现了新名词:语句项,这是个什么鬼?
语句项就是case语句,语法结构如下:
case 整型常量表达式:
语句;
可以发现,case和switch是嵌套着使用的
除此之外,还有一个叫做break的语句,搭配着就可以实现分支
case表示程序入口,break表示程序的出口。
2.2.1举个🌰:
比如我们输入一个数字,1是进清北,2是进c9,3是进985,4是进211,5是进双一流,6是进一本,7是进二本,8是进大专。
那么用switch语句是这样的:
#include <stdio.h>
int main()
{
int choise = 0;
switch (choise)
{
case 1:
printf("清北\n");
break;
case 2:
printf("c9\n");
break;
case 3:
printf("985\n");
break;
case 4:
printf("211\n");
break;
case 5:
printf("双一流\n");
break;
case 6:
printf("一本\n");
break;
case 7:
printf("二本\n");
break;
case 8:
printf("专科\n");
break;
}
return 0;
}
成功运行
如果仅用来区分本科专科,可以这样写:
int main()
{
int choise = 0;
scanf("%d", &choise);
switch (choise)
{
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
printf("本科\n");
break;
case 8:
printf("专科\n");
break;
}
return 0;
}
这样一来,无论是1~7哪个数字,直接进入之后就到printf打印“本科”那里了。
所以,break的作用其实是用来划分不同的分支的,而不是划分类型,不一定每个case后都要写break。
tips:最好在最后一个case语句上面加上一条break语句,避免之后再写case的时候和之前写的case有交叉,有break的话就可以直接把他们分开啦。
2.2.2 default 子句
在if语句中,如果我们输入了错误的值,可以用else来进行分支,那么在switch语句中我们如何解决这种输入错误的情况呢?
答案是:使用default子句
还是使用上文的栗子:
#include <stdio.h>
int main()
{
int choise = 0;
scanf("%d", &choise);
switch (choise)
{
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
printf("本科\n");
break;
case 8:
printf("专科\n");
break;
default:
printf("输入错误\n");
}
return 0;
}
可见,只需要加上一个default的子句即可。而default和case这些子句都是没有顺序的,可以在switch语句里面随意调换顺序(一般情况下是栗子里面的顺序,方便代码的阅读)。
tips:在switch后加上一个default是一个好习惯,在其后再加上一个break就更好了。
2.2.3 switch语句最后的小练习:
请你计算m和n的值(时间3分钟以内为宜,难度口算即可)
#include <stdio.h>
int main ()
{
int n = 1;
int m = 2;
switch (n)
{
case 1:
m++;
case 2:
n++;
case 3:
switch (n)
{//switch允许嵌套使用
case 1:
n++;
case 2:
m++;
n++;
break;
}
case 4:
m++;
break;
default:
break;
}
printf("m = %d, n = %d\n", m, n);
return 0;
}
正确答案的分割线(不要往下划啦,做完再划)
正确答案:
你算对了吗?
3、循环语句(循环结构)
在循环结构中,有三个语句:
- while
- for
- do while
循环结构如下图:
3.1 while循环
3.1.1语法结构
while (exp)
循环语句;
while后面的表达式是一个判断,和if的判断方法一样,不过while是一直判断,如果是真就执行后继续去判断表达式,直到表达式不是真(即==0
)。
举个栗子:打印数字1-10:
int main()
{
int a = 1;
while (a <= 10)
{
printf("%d ", a);
a++;
}
return 0;
}
运行结果
成功运行
3.1.2 while语句中的break和continue
- break:直接结束while循环(永久终止循环)
- continue:结束本次循环(直接进行下次循环)
3.1.2.1举个break的🌰:
#include <stdio.h>
int main()
{
int a = 1;
while (a <= 10)
{
if (a == 5)
break;
printf("%d ", a);
a++;
}
return 0;
}
运行结果:
可以看出,在a加到5的时候,因为break语句直接就退出了循环,只打印了1 2 3 4 。
3.1.2.2举个continue的🌰:
int main()
{
int a = 1;
while (a <= 10)
{
if (a == 5)
continue;
printf("%d ", a);
a++;
}
return 0;
}
对刚刚的代码稍加改造,那么这个代码的结果是多少呢?
答案是:
1 2 3 4 之后就是死循环,和break不同,程序并没有结束,一直在跑,没有尽头。
为什么会出现这种情况呢?
我们在代码上进行一些标注:
红色代表第五次while循环,粉色代表第六次while循环
可以看到,当a到达5的时候,一到continue便开始了无休止的循环,既没有结果也不会停止。
我们把a++放到前面再试一试:
int main()
{
int a = 1;
while (a <= 10)
{
a++;
if (a == 5)
continue;
printf("%d ", a);
}
return 0;
}
- 运行结果:
因为顺序的调换,变成了2打头,而5因为continue,跳过了,最后因为a++在printf前面的关系,直到打印11才停下来。
3.1.3利用getchar进行清理缓冲区
像scanf、getchar这类函数,是可以根据键盘上输入的字符来进行输入的,但是它们并不是直接打进程序里面去,而是先进到一个叫做输入缓冲区的地方,如图:
-
那么在某些需要重复输入不同信息的情况下,缓冲区的信息可能还没被清理干净,就被读走了,就容易造成程序的bug。
-
比如你输入一段数字,按了回车或者空格之后scanf读走了,之后再来个别的需要读取的函数,他不会自动的把缓冲区清零,而是直接把回车读走,也就是把
\n
读走,各种各样奇妙的bug就此诞生。 -
既然缓冲区这么重要,那么如何清理它呢?
-
我们可以利用while循环,加上一点点getchar的知识:
- getchar是获取字符的关键字,通俗理解就是把字符读取走,拿走的意思。
-
举个🌰:
#include <stdio.h>
int main()
{
//字符类型的数组,定义密码,以后会详细讲
char password[20] = { 0 };
printf("请输入密码--->");
//%s表示打印字符串
scanf("%s", password);
printf("请确认密码--->");
char input = 0;
//下面这个while循环是重点,利用空语句让getchar不断的把回车之前的字符都读走,直到把回车读走,缓存区也就清空了
while (getchar() != '\n')
{
;
}
//%c表示打印单个字符
//这里用input=getchar();也可以
scanf("%c", &input);
if ('Y' == input)
printf("确认成功\n");
else
printf("确认失败\n");
return 0;
}
运行结果:
可以看到,不论是空格还是字母都不会出现因为缓存区不干净直接跳过输入的bug
3.2 for 循环
3.2.1语法结构
for(exp1;exp2;exp3)
循环语句;
其中exp1用于初始化,exp2用于条件判断,exp3用于调整条件
如图所示:
for和if语句一样,默认后面只能跟一个语句,如果想同时执行多条语句,则需要加大括号。
3.2.2for里面的break和continue
和while语句里面有异曲同工之妙:break仍是永久终止循环直接跳出去;continue仍是跳过本次循环。
3.2.3 for的循环控制变量
- 不可以在循环体内修改变量
- 建议for语句的取值采用“前闭后开区间”的写法,更加直观
3.2.4 for语句变种
- 省略for结构里的某一部分,或者全部省略
- 可能会导致某些bug
- 使用多个变量控制循环
- 如:for(x=0,y=0;x<2 && y<5;++x,y++)
3.3 do…while()循环
至少循环一次
语法结构:
do
循环语句;
while(表达式);
4.goto语句
在C语言中,有一个语句比较特殊,它可以随意跳转到程序任意地方,这就是goto语句。
虽然这个语句的功能非常强大,但是在实际使用过程中,由于其过于强大,想跳到哪就跳到哪,很容易出现程序运行出现死循环或者bug之类的故障。并且,一般情况下,其他几个分支循环语句就已经足够写程序用了,不需要用goto语句。
goto语句最常见的用处就是在深度嵌套的代码中,实现跳出。
写在最后
如果本文对您有帮助,可不可以给我一个小小的点赞呀❤~您的支持是我最大的动力。
博主小白一枚,才疏学浅,难免有所纰漏,欢迎大家讨论和提出问题,博主一定第一时间改正。
谢谢观看嘿嘿(๑•̀ㅂ•́)و✧~!