目录
3.3.2.do while循环中的break和continue
3.4.练习(※)
1.什么是语句
控制语句 用于控制程序的执行流程,以实现程序的各种结构方式,它们由特定的语句定义符组成, C 语言有九种控制语句。可分成以下三类:1. 条件判断语句也叫分支语句: if 语句、 switch 语句;2. 循环执行语句: do while 语句、 while 语句、 for 语句;3. 转向语句: break 语句、 goto 语句、 continue 语句、 return 语句。
2.分支语句(选择结构)
·if语句
·switch语句
2.1.if语句
1.单分支if语句
if ( 表达式 )语句 ;
if ( 表达式 )语句 1 ;else语句 2 ;
if ( 表达式 1 )语句 1 ;else if ( 表达式 2 )语句 2 ;else语句 3 ;
#include<stdio.h>
int main()
{
int age=0;
scanf("%d",&age);
if(age>=18)
printf("成年\n");
return 0;
}
代码2:
#include<stdio.h>
int main()
{
int age=0;
scanf("%d",&age);
if(age>=18)
printf("成年\n");
else
printf("未成年\n");
return 0;
}
代码3:
#include<stdio.h>
int main()
{
int age=0;
scanf("%d",&age);
if(age<18)
printf("少年\n");
else if(age>=18 && age<25)
printf("青年\n");
else if(age>=25 && age<60)
printf("壮年\n");
else if(age>=60 && age<90)
printf("老年\n");
else
printf("老寿星\n");
return 0;
}
#include<stdio.h>
int main()
{
int age=0;
scanf("%d",&age);
if(age>=18)
{
printf("成年\n");
printf("可以谈恋爱\n");
}
else
printf("未成年\n");
return 0;
}
2.1.1.悬空else
代码1:
#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; }
运行结果1:无输出
代码2:
#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; }
运行结果2:
注:
1.else总是和离得最近的if相匹配。
2.第一个代码的风格不好,使用{ }可以使阅读效果更好。
2.1.2.if书写形式的对比
代码1:
if (condition) {
return x;
}
return y;
if(condition) {
return x; }
else
{
return y;
}
注:两段代码意思相同,都是条件成立返回x,不成立返回y。但是代码1是不好的代码,可读性差。
int num = 1;
if(num == 5)
{
printf("hehe\n");
}
代码4:
int num = 1;
if(5 == num)
{
printf("hehe\n");
}
注:代码4更好,因为假如你疏忽将==写成了=,则代码1不会报错,而代码2因为给一个常量赋值,系统会报错。
2.1.3练习
1. 判断一个数是否为奇数2. 输出 1-100 之间的奇数
2.2.switch语句
switch ( 表达式 ){case 表达式1 :语句1 ;case 表达式2 :语句2 ;}
2.2.1.在switch语句中的break
代码1:
#include <stdio.h>
int main()
{
int day = 0;
switch(day)
{
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;
}
#include <stdio.h>
//switch代码演示
int main()
{
int day = 0;
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;
}
2.2.2.default子句
#include <stdio.h>
int main()
{
int day = 0;
switch(day)
{
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;
default:
printf("输入错误,请输入1~7的数字\n");
break;
}
return 0;
}
注:
1.default:默认前面的case都不能匹配的情况下,那就走default的语句。
2.default一般是放在最后,放在前面也可以。
3.不需要default时,default可以不用删除(在每个 switch 语句中都放一条default子句是个好习惯),把default中的语句删除即可(default要是在前面default中的break要加上,要是在末尾,break可删除)
2.2.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;
}
2.2.4.总结
switch语句运行原理:
从前往后case后的表达式与switch后表达式匹配:(两种情况)
1. 匹配上了:从匹配上的case语句往后所有语句依次执行(包括default后的语句)(不再与后面的case匹配),直到遇见break。
2.未匹配上: 找default,从default语句往后所有语句依次执行,直到遇见break。
1.语句中switch后面括号内可以是整型常量、整型常量表达式、字符常量、字符型常量表达式或enum类型。(字符型常量、字符型常量表达式其实也是整型常量、整型常量表达式,因为字符是有ASCII码值的,字符在存储的时候,存储的是ASCII值)
2.case后面可以是整型常量、整型常量表达式、字符常量、字符型常量表达式。
3.若default放在最后,匹配后不再执行其他分支,因此可不加break。若放在前面,则需要加break。
4.default不是必须的,若没有default,如果所有case匹配失败,那么什么都不执行。
3.循环语句
·while·for·do while
3.1.while循环
3.1.1.语法
while ( 表达式 ){循环语句 ;}
#include <stdio.h>
int main()
{
int i = 1; //初始化
while(i<=10) //判断
{
printf("%d ", i);
i++; //调整部分
}
return 0;
}
3.1.2.while语句中的break和continue
#include <stdio.h>
int main()
{
int i = 1;
while(i<=10)
{
if(5==i)
break;
printf("%d ", i);
i++;
}
return 0;
}
#include <stdio.h>
int main()
{
int i = 1;
while(i<=10)
{
if(5==i)
continue;
printf("%d ", i);
i++;
}
return 0;
}
运行结果:死循环
3.1.3.几个重要代码范例(※)
代码范例1:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
int ch = getchar();
printf("%c\n", ch);
putchar(ch);
return 0;
}
注:
1.getchar函数
上图stdin意思是标准输入,意思就是键盘。函数作用就是可以利用getchar函数从键盘获取一个字符。
2.getchar函数返回字符的类型一般是整型。getchar取得是一个字符,但字符的存储值(代表这个字符的值)是一个ASCII值,ASCII值是一个整数,因此可以用整型来接收。当getchar读取失败的时候,返回EOF,如下图,其实EOF就是-1,而-1是一个整型,因此getchar最好用整型来接收。若用char接受,当getchar读取失败时,char只有一个字节无法接受4个字节的-1值
3.putchar函数
上图stdout的意思是标准输出,意思就是屏幕。函数作用就是可以利用putchar函数在屏幕上输出一个字符。
4.getchar函数和putchar函数只是针对字符的输入和输出。scanf函数和printf函数比这两个函数功能要全。
#include <stdio.h>
int main()
{
int ch = 0;
while ((ch = getchar()) != EOF)
{
putchar(ch);
}
return 0;
}
运行结果2:
注:
1.若要让这个代码运行停下来,使用ctrl+z,ctrl+z意思就是获取一个EOF,代码中EOF!=EOF逻辑为假,代码结束。
2.这里要注意括号,(ch = getchar()) != EOF 与 ch = getchar() != EOF表达的逻辑是不一样的,前面是将输入值赋值给ch再与EOF进行比较,后面代码是将输入值先与EOF进行比较,再将比较的结果赋值给ch。
3.getchar具体讲解如下,这里的代码适当的修改是可以用来清理缓冲区的,如下面代码范例3所示。
- 实际上getchar函数是从缓冲区读取数据的,而不是从你的屏幕上读取数据.
- 键盘负责往缓冲区写入数据,而getchar函数负责读取数据.
- getchar函数不会关心你实际输入的是什么,它只负责从缓冲区读取数据,而且每次读取一个字符.
- 你点击键盘的时候,实际上是往缓冲区输入:
- 你按下a,则缓冲区中保存了一个字符a
- 你按下b,则缓冲区中又保存了一个字符b
- 你按下c,则缓冲区中又保存了一个字符c
- 你按下1,则缓冲区中又保存了一个字符1
- 你按下backspace删除键,则缓冲区中删除了一个字符(最后保存的那个1)
- 除非你按下Enter,不然你其实就是在往缓冲区中塞数据.
- 关键点来了:当你按下Enter的时候,发生了两个动作.首先是往缓冲区保存了一个'\n'字符,然后才执行刷新
- 刷新其实就是告诉getchar(或者任何需要读取这个缓冲区的函数,比如scanf),现在你可以开始读取我保存的数据了.
于是现在缓冲区中保存的数据是[a,b,c,'\n'] ,注意缓冲区的最后会有一个换行符(因为你敲了Enter键). 好,现在开始读取数据:
- 第一次调用getchar,则读取缓冲区中的第一个数据a
- 第二次调用getchar,将会读取缓冲区中的第二个数据b
- 第三次调用getchar,将会读取缓冲区中的第三个数据c
- 第四次调用getchar,将会读取缓冲区中的最后一个数据'\n',也就是换行符.
如果你while循环来调用n次getchar(),就会按顺序读取缓冲区n次,就好像是按顺序读取数组一样.
如果getchar()发现缓冲区中没数据了,也就是说缓冲区的数据已经被读完了,没数据可读了.那么getchar函数会等你再次输入数据并且按下Enter执行刷新.
如果缓冲区有8个字符,你却只调用了5次getchar,那么缓冲区会剩下3个字符.因为你只读了5个字符.
代码范例3:
注:
1.上图第1个代码无法达到预期的目的,此时输入abcdef还没有进入确认密码阶段,系统就已经输出确认失败了。事实上scanf函数和getchar函数不是从键盘上拿取数据的,而是从输入缓冲区拿取数据,scanf和getchar的工作原理是它去看输入缓冲区有没有东西,若有东西,函数就拿走了,若 输入缓冲区什么都没有,他就会等待你输入。代码1中,你输入abcdef还需要按回车,敲回车的话相当于加了一个\n,scanf读取拿走了abcdef给了input变量,此时缓冲区还有个\n,getchar再获取信息读取了缓冲区的\n赋值给ch,\n不等于Y,因此直接输出了确认失败。
2.上图第2个代码在第一个代码代码的基础上加了一个getchar,此时getchar的作用是从缓冲区拿走一个字符,现在getchar把多余的\n取走,便可达到预期目的。但是scanf(%s,...)读取字符串的时候,遇到空格就结束了,因此发生了代码2的错误,此时scanf拿走了abcdef并结束了,后面的getchar拿走了输入缓冲区后面的一个字符h,再后面的getchar拿走了e赋值给ch并与Y比较,逻辑为0,输出确认失败。
3.如上图第3个代码所示,把getchar换成一个while循环语句,便可将缓冲区里的所有字符(包括\n)全部拿走,此时再输入一个值就赋值给了ch进行判断,达到预期目的。
4.如上图第一个代码中输入函数括号内所示,数组名本来就是地址,因此数组名是不需要取地址的。
代码范例4:
#include <stdio.h>
int main()
{
int ch = 0;
while ((ch = getchar()) != EOF)
{
if (ch < ‘0’ || ch > ‘9’)
continue;
putchar(ch);
}
return 0;
}
注:这个代码的作用是:只打印数字字符,跳过其他字符的。
3.2.for循环
3.2.1.语法
for语句语法结构:
for ( 表达式 1 ; 表达式 2 ; 表达式 3 ){循环语句 ;}表达式 1:表达式 1 为 初始化部分 ,用于初始化循环变量的。表达式 2:表达式 2 为 条件判断部分 ,用于判断循环时候终止。表达式 3:表达式 3 为 调整部分 ,用于循环条件的调整
#include <stdio.h>
int main()
{
int i = 0;
for(i=1; i<=10; i++)
{
printf("%d ", i);
}
return 0; }
3.2.2.for语句中的break和continue
#include <stdio.h>
int main()
{
int i = 0;
for(i=1; i<=10; i++)
{
if(i == 5)
break;
printf("%d ",i);
}
return 0;
}
运行结果:
#include <stdio.h>
int main()
{
int i = 0;
for(i=1; i<=10; i++)
{
if(i == 5)
continue;
printf("%d ",i);
}
return 0;
}
运行结果:
总结:
3.2.3.for语句的循环控制变量
1.不可在for 循环体内修改循环变量,防止 for 循环失去控制。(见如下代码)
代码:
运行结果:死循环
2. 建议for语句的循环控制变量的取值采用“前闭后开区间”写法。(见如下代码)
注:这样写不好,代码的可读性不高,建议写成前闭后开区间。
3.2.4.一些for循环的变种
1. for循环的变种一
代码:
#include <stdio.h>
int main()
{
for(;;)
{
printf("hehe\n");
}
int x, y;
for (x = 0, y = 0; x<2 && y<5; ++x, y++)
{
printf("hehe\n");
}
return 0;
注:for循环也可以用两个变量来进行控制。
3.2.5.练习
请问循环要循环多少次?
#include <stdio.h>
int main()
{
int i = 0;
int k = 0;
for(i =0,k=0; k=0; i++,k++)
k++;
return 0; }
答案:0次(for循环条件判断部分为k=0,其结果是0为假,不进入循环)
3.3.do while循环
do while语句循环至少执行一次,使用的场景有限,所以不是经常使用。
3.3.1.语法
do while语句语法结构:
do循环语句 ;while ( 表达式 );
代码:
#include <stdio.h>
int main()
{
int i = 1;
do
{
printf("%d\n", i);
i++;
} while(i<=10);
return 0;
}
3.3.2.do while循环中的break和continue
运行结果:
运行结果:死循环
总结:
3.3.3.do语句的特点
#include <stdio.h>
int main()
{
int i = 10;
do
{
printf("%d\n", i);
}while(i<10);
return 0;
}
3.4.练习(※)
1. 计算 n 的阶乘。2. 计算 1!+2!+3!+……+10!3. 在一个有序数组中查找具体的某个数字 n 。(讲解二分查找)4. 编写代码,演示多个字符从两端移动,向中间汇聚。5. 编写代码实现,模拟用户登录情景,并且只能登录三次。(只允许输入三次密码,如果密码正确则提示登录成,如果三次均输入错误,则退出程序。6.实现猜数字游戏:电脑随机生成一个1~100之间的数字,玩家猜数字,若玩家猜小了,电脑告诉猜小了,若玩家猜大了,电脑告诉猜大了,若玩家猜对了,电脑告诉恭喜你,猜对了。
练习1参考代码:
练习2参考代码:
错误代码:
普通正确代码:
优化的正确代码:
练习3参考代码:
普通正确代码:(在1~10中找到7所在的位置)
优化正确代码:(二分查找、折半查找)(在1~10中找到7所在的位置)
练习4参考代码:
注:
1.sleep():睡眠函数,其单位是毫秒,使用sleep需要windows.h的头文件。
2.system():用来执行系统命令。
3.cls:系统命令,其功能是清理屏幕(清空屏幕)。
练习5参考代码:(假设密码是123456)
注:
1.scanf中password不取地址的原因是password是数组名,数组名本来就是地址。
2.比较两个字符串是否相等,不能使用==而应该使用strcmp这个函数,strcmp函数返回0说明两个字符串相等 。
3.使用strcmp函数需要string.h头文档。
练习6参考代码:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include <stdlib.h>
#include <time.h>
void menu()
{
printf("***************************\n");
printf("***** 1. play ******\n");
printf("***** 0. exit ******\n");
printf("***************************\n");
}
void game()
{
int guess = 0;
//猜数字游戏的过程
//生成随机数
int r = rand() % 100 + 1;//0~99-->1~100
//猜数字
while (1)
{
printf("猜数字:>");
scanf("%d", &guess);
if (guess < r)
{
printf("猜小了\n");
}
else if (guess > r)
{
printf("猜大了\n");
}
else
{
printf("恭喜你,猜对了\n");
break;
}
}
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL));//时间-设置随机数的生成器
do
{
//打印菜单
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误\n");
break;
}
} while (input);
return 0;
}
注:
1.c语言中生成的随机数的方式是rand()函数,返回值是一个整数,整数的范围是0~RAND_MAX(见下面rand介绍),RAND_MAX的数值是十六进制数字的0x7fff(下面见RAND_MAX转定义),转换成十进制数是32767。
MSDN中rand介绍:
将RAND_MAX转定义可以看到:
2.在调用rand()函数之前,需要先调用srand(),srand()功能是设置随机数的生成器。想要用srand()和rand()生成随机数,给srand()一个随机变化的数(给的数必须是unsigned int类型)即可,比如时间戳就是在不停的变化(给定一个时间时间比如2021-10-28 21点32分34秒,这个时间的时间戳就是这个时间和计算机起始时间的一个差值转化成秒)。
3.c语言中time()函数可以返回一个时间戳(该返回值为一个整型),使用时可以给time一个空指针:time(NULL),NULL就是空指针的意思。因为time函数返回值是整型,所以给到srand()函数时,需要转化成unsigned int类型,即srand((unsigned int)time(NULL))
4.srand((unsigned int)time(NULL))在整个程序里面只要设置一次就行,每次需要生成随机数单独调用rand函数即可,若多次不停的快速将srand和rand绑定使用生成随机数,生成的前后随机数可能相同,因为时间戳来不及改变。
5.使用rand()函数需要添加头文件是stdlib.h,使用time()函数需要添加头文件是time.h
4.goto语句
C 语言中提供了可以随意滥用的 goto 语句和标记跳转的标号。从理论上 goto 语句是没有必要的,实践中没有 goto 语句也可以很容易的写出代码。但是某些场合下 goto 语句还是用得着的,最常见的用法就是终止程序在某些深度嵌套的结构的处理过程。例如:一次跳出两层或多层循环。多层循环这种情况使用 break 是达不到目的的。它只能从最内层循环退出到上一层的循环。
4.1.goto的用法
代码:
注:
1.again是一个跳转标签,可以随意起名字。
2.跳转标签和goto必须在一个函数里面。
4.2.goto语言真正适合的场景
for(...)
for(...)
{
for(...)
{
if(disaster)
goto error;
}
}
…
error:
if(disaster)
注:使用goto语句可以直接跳出多层循环嵌套。
4.3.使用循环的实现方式替换goto语句的例子(※)
写一个关机程序:只要程序启动了,就倒计时60秒关机,如果60秒内你输入了我是猪,就取消关机,如果不输入,时间到关机。
shutdown -s :设置关机shutdown -s -t :设置时间来关机shutdown -s -t 60:设置时间60秒后关机shutdown -a:取消关机
4.自己编写的这种exe程序可以设置成自启动,在电脑中输入服务,在服务中,所有的文件都是exe类型,可以将自己写的exe程序加入到这里面(方法,百度搜索如何将自己的exe程序加入到服务即可),然后将添加的程序选中点右键点属性,出现的对话框中点击启动类型,里面有自动(电脑开机自动启动)和手动(双击才能运行),选中自动就实现了开机自启动。(如果本代码被设置成了开机自启动,开机后必须以最快速度打开cmd输入取消关机代码。)