1、什么是语句
C语言语句可以分为五类:
- 表达式语句
- 函数调用语句
- 控制语句
- 复合语句
- 空语句
C语言有九种控制语句
可以分成一下三类:
- 条件判断语句也叫分支语句:if语句,switch语句;
- 循环执行语句:do while语句、while语句、for语句;
- 转向语句:break语句、goto语句、continue语句、return语句。
2、if语句
if语句的语法结构:
if (表达式) //表达式结果如果为真或者非0,那这个表达式就会运行
语句;
if (表达式)
语句; //这里如果只有一条语句,那就不需要带{},也可以带
else
语句2;
if (表达式)
语句1; //这里大于一条语句,那就需要带{}
语句2;
else
语句2;
//多分支
if (表达式1)
语句;
else if(表达式2)
语句2;
else
语句3;
//注意:如果一个表达式同时满足if语句有满足else if语句,因为if语句先接收的这个表达式,那这个表达式只会进入到if语句里面。
2.1、if是和靠的最近的else相匹配的
下面先来看段代码:
#include <stdio.h>
int main()
{
int a = 0;
int b = 2;
if (a == 1)
if (b == 2)
printf("hehe\n");
else
printf("haha\n");
return 0;
}
输出:
显而易见这里的输出竟然为空!!!
这是问什么呢?不应该是输出:haha 的结果吗?为什么不对呢?
这是因为else是会和最近的if进行匹配,所以输出为空。
所以说代码的结构应该是这样:
#include <stdio.h>
int main()
{
int a = 0;
int b = 2;
if (a == 1)
if (b == 2) //它们的缩进在一起的!!!
printf("hehe\n");
else //它们两个是一对
printf("haha\n");
return 0;
}
总结:else和if是靠的最近进行匹配的,不是说else和if在同一缩进进行匹配的。
3、switch语句
switch语句也是一种分支语句
常常用于多分支的情况
比如:
输入1,输出星期一
输入2,输出星期二
输入3,输出星期三
输入4,输出星期四
输入5,输出星期五
输入6,输出星期六
语法:
switch (整型表达式)
{
语句项;
}
而语句项是什么呢?
//语句项是一些case语句
//如下:
case 整形常量表达式 //case后面必须是整形常量,当然使用字母表示也行,因为字母的ASCII也是整数
语句;
使用switch实现星期查询
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int day = 0;
scanf("%d", &day);
switch (day)
{
case 1:
printf("星期一\n");
break;
case 2:
printf("星期二\n");
break;
case 3:
printf("星期三\n");
case 4:
printf("星期四\n");
break;
case 5:
printf("星期五\n");
break;
case 6:
printf("星期六\n");
break;
case 7:
printf("星期日\n");
break;
}
return 0;
}
说下switch原理:
从switch中输入相对应的编号,根据编号找到相对应的case语句,这样就算进入switch语句了,然后匹配后经过break语句出switch语句。
3.1、新的需求
要求:
- 输入1-5,输出“weekday”
- 输入6-7,输出“weekend”
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int day = 0;
scanf("%d", &day);
switch (day)
{
case 1:
case 2:
case 3:
case 4:
case 5:
printf("weekday\n");
break;
case 6:
case 7:
printf("weekend\n");
break;
}
return 0;
}
这说明case语句在没有遇到break语句之前是一下向下执行,知道遇见break语句才会跳出switch语句。
3.2、default字句
如果表达的值与所有的case标签的值都不匹配怎么办?
其实也没什么,都不匹配最终肯定是会全部跳过而已。
程序也不会终止,也不会报错,但是当在都不匹配的情况下我们最后提示一下。
那这就需要default字句了。
注意:switch语句中只能出现一个default字句
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int day = 0;
scanf("%d", &day);
switch (day)
{
case 1:
case 2:
case 3:
case 4:
case 5:
printf("weekday\n");
break;
case 6:
case 7:
printf("weekend\n");
break;
default: //所示
printf("输出错误");
break;
}
return 0;
}
输出:
4、循环语句
- while
- for
- do while
4.1、while
语法结构
while (表达式)
{
循环语句;
}
4.1.1、break语句
只要while循环中执行了break语句,那么整个while循环将会停止,这个break语句在while循环的那任何部分中,while都会立即终端
代码示例:
#include <stdio.h>
int main()
{
int a = 1;
while (a < 11)
{
if (5 == a)
{
break; //这个break语句看着是在if语句中。但是一旦它被执行,那么整个while语句将会停止
}
printf("%d\n",a);
a++;
}
return 0;
}
输出:
4.1.2、continue语句
一旦在while语句中执行了continue语句,那么此次循环将会直接跳过continue后面的代码,然后判断循环,如果继续则直接进行下一次循环(就直接从while (a<11)处开始执行)。
#include <stdio.h>
int main()
{
int a = 1;
while (a < 11)
{
if (5 == a)
{
continue; //当a=5时,不在执行后面的代码,直接跳过从while循环开始
}
printf("%d\n",a);
a++;
}
return 0;
}
输出:一直死循环的输出:1 2 3 4
4.2、使用while循环清理缓冲区
4.2.1、getchar()和putchar()
- getchar()函数用于从键盘上读取数据和scanf一样
- putchar()函数用于输出数据
#include <stdio.h>
int main()
{
int ch = getchar(); //我们在键盘中输入“e”,这个e会被转为相应的ASCII值存入ch中
printf("%c\n", ch); //然后在转为字符(%c)进行输出
putchar(ch); //这个可以直接输出,和上面的打印的效果是一样的,这是另一种方法
return 0;
}
输出:
但是这里我们有个疑问:
int ch = getchar();
明明getchar()接收的是个字符,为什么我们要用int类型来接收呢?为什么不用char类型接收呢?
这里解释一下:
因为在读到字符时,会转为相应的ASCII码值,所以说放在int类型是没有问题的。
重点:如果getchar()在读取过程中遇到了错误/不正常或者遇到了文件结束,那么这个函数会返回个EOF
EOF--------end of file文件结束标志
我们来看一下EOF的定义(define)
#define EOF (-1)
可以看到EOF定义的值是-1,-1是个整数,所以说我们接收getchar()返回的数据就应该用int类型去接受
那好知道了这些我们就可以用while循环来实现清理缓冲区的功能:
#include <stdio.h>
int main()
{
int ch = 0;
while ((ch = getchar()) != EOF) //这里ch接收getcha返回的数据不等于EOF,说明返回的是个正常的字符,那就可以正常输出
{
putchar(ch);
}
return 0;
}
输出:
会发现输出不会停止,这是Ctrl+z,然后在回车就可以终止程序了。
Ctrl+z就是让getchar返回一个EOF,这样程序就终止了
【重点:】下面来好好理解一下这个程序的逻辑:
这里先提出个问题?为什么上面的输出会自动换行?
这里设计到缓冲区
的概念
我们所知道的scanf
,getchar
函数来从键盘上获取数据,是我们从键盘上一输出字符,scanf
,getchar
函数就能接收到的吗?其实并不是,正确的顺序是:我们在键盘上输出字符,这个字符会进入缓冲区,此时scanf
,getchar
函数检测到缓冲区有字符,这个时候scanf
,getchar
函数才会把该字符从缓冲区中拿出来。
那为什么会自动换行呢?
还是上面代码流程分析:当我们输入“a”并且回车后,(回车相当于输入了\n),首先判断a!=EOF,因此执行putchar(ch)然后打印出“a”,然后再次循环,因为缓冲区里里面“a”已经被读取了,所以还剩个“\n”,然后getchar直接读取,因为“\n”!=EOF,所以在次执行putchar(ch),所以就会自动换行了。
4.2.2、深度解析—清理缓冲区这段代码
先来看段代码:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
char password[20] = { 0 };
printf("请输入密码:>");
scanf("%s", password); //这里不用在&password,因为数组名就是数组首元素地址,所以不用取地址
printf("请确认密码(Y/N):>");
int ret = getchar();
if (ret == 'Y')
printf("Yes\n");
else
{
printf("No\n");
}
return 0;
}
输出:
显然这样的输出结果是不对的,明明我还没确认,程序直接就打印了NO
分析:当我们在键盘中输入:abcdef,然后回车(\n),此时输入缓冲区里面有了:“abcdef\n”,scanf指挥取它所需要的。因此scanf把“abcdef”给拿走了,因此password=“abcdef”,但是此时缓冲区里面还有个“\n”,然后程序向下走getchar获取数据,一看缓冲区里面还有个“\n”,因此getchar直接拿走了“\n”,然后进入if判断,“\n”!=‘Y’,所以程序输出No。
那怎么解决呢?我们想办法只要让缓冲区里面没有字符就行了,可以这样实现:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
char password[20] = { 0 };
printf("请输入密码:>");
scanf("%s", password);
getchar(); //这里在添加个getchar(),提前把那个\n读取掉,那下面那个getchar()就可以正确判断了
printf("请确认密码(Y/N):>");
int ret = getchar();
if (ret == 'Y')
printf("Yes\n");
else
{
printf("No\n");
}
return 0;
}
但是这种写法是比较粗鲁的,不适用每一种情况。比如:我们在键盘中输入:abcdef sdf(中间有个空格)
这样又不行了。我们来分析一下为什么又出错了:
首先scanf会读取缓冲区里面的“abcdef”,也就是说scanf会读取到空格前,紧接着第一个getchar()会读取空格,那就相当于第一个getchar()被使用了,然后下面的getchar()会读取到sdf,因为’sdf’ != ‘y’,所以会输出No。
这是我们能感知到,第一个错误的解决办法是需要清理缓冲区里面的一个字符,而这个是需要清理缓冲区里面的许多个字符才能解决,因此我们需要循环解决。
我们可以循环使用getchar()把空格到“sdf”以及后面的“\n”全部读取掉,这样就行了
下面我们来实现个正确的方法:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
char password[20] = { 0 };
printf("请输入密码:>");
scanf("%s", password);
int ch = 0; //start
while ((ch = getchar()) != '\n')
{
;
} //end
printf("请确认密码(Y/N):>");
int ret = getchar();
if (ret == 'Y')
printf("Yes\n");
else
{
printf("No\n");
}
return 0;
}
5、for循环
5.1、语法
for (表达式1;表达式2;表达式3)
{
循环语句;
}
__表达式一:__表达式一为初始化部分,用于初始化循环变量的。
__表达式二:__表达式二为条件判断部分,用于判断循环时终止。
__表达式三:__表达式三为调整部分,用于循环条件的调整。
for循环执行顺序:
第一遍循环:
第2、3、4、5、6…循环:
5.2、break,continue
在for循环和while循环里面break语句的效果一样。
同样在for循环和while循环里面continue语句有一点小差别
如下图:
其中for循环在遇见continue语句后是跳过后面的代码,然后跳到“i++”(调整)中去了
二while循环在遇见continue语句后是跳到了判断里面去了。
5.3、for语句的循环控制变量
建议:
- 不可在for循环体内修改循环变量,防止for循环失去控制。
- 建议for语句的循环控制变量取值采用“前闭后开区间”写法。
5.4、for循环的变种
1、死循环
#include <stdio.h>
int main()
{
for (;;)
{
printf("hehe\n");
}
return 0;
}
__结论:__for循环的判断部分省略意味着判断或恒成立,而初始化,调整省略没事(但最好不要省略)。
2、for循环采用多个变量去控制
#include <stdio.h>
int main()
{
int x, y;
for (x = 0, y = 0; x < 2 && y>5; ++x, y++)
{
printf("hehe\n");
}
return 0;
}
6、do-while循环
6.1、do语法
do
{
循环语句;
}
while(表达式);
打印1-10:
#include <stdio.h>
int main()
{
int i = 1;
do
{
printf("%d ", i);
i++;
} while (i <= 10);
return 0;
}
do循环最少循环一次。
6.2、break和continue
作用一样。
7、goto语句
C语言中提供了可以随意滥用的goto语句和标记跳转的标号
从理论上goto语句是没有必要的,实践中没有goto语句也可以很容易的写出代码
但是在某些场合下goto语句还是得用得着的,最常见的用法就是终止程序在某些深度嵌套的结构的处理过程。
例如:一次跳出两层或多层循环。
多层循环这种情况使用break是达不到目的的,它只能从内层循环到上一层的循环。
先来看一下goto的效果:
#include <stdio.h>
int main()
{
again: //标签
printf("hehe\n");
printf("haha\n");
goto again;
return 0;
}
//但是goto不能跨函数跳转,如下就是不行的
void test()
{
again;
}
int main()
{
printf("hehe\n");
printf("haha\n");
goto again;
return 0;
}
goto语句真正适合的场景下:
for (...)
{
for (...)
{
for (...)
{
if (disaster)
goto error; #这样可以根据error标签直接跳过两层for循环
}
}
error:
if (disaster)
//处理错误情况
}
7.1、使用goto语句模拟关机程序
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <Windows.h>
#include <string.h>
int main()
{
char input[20] = { 0 };
system("shutdown -s -t 60"); //电脑在60s内关机
again:
printf("电脑即将在60秒内关机,如果不想关机,输入:我是猪,即可解决。\n");
scanf("%s", input);
if (strcmp(input, "我是猪") == 0)
{
system("shutdown -a");
printf("已取消关机\n");
}
else
{
goto again;
}
return 0;
}
输出效果:
当然我们也可以使用循环的方式替代goto
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <Windows.h>
#include <string.h>
int main()
{
char input[20] = { 0 };
system("shutdown -s -t 60"); //电脑在60s内关机
while (1)
{
printf("电脑即将在60秒内关机,如果不想关机,输入:我是猪,即可解决。\n");
scanf("%s", input);
if (strcmp(input, "我是猪") == 0)
{
system("shutdown -a");
printf("已取消关机\n");
break;
}
}
return 0;
}