- 1.前言
- 2.分支语句
- 3.循环语句
- 4.练习题
- 5.goto语句
本文将详细介绍C语言中控制语句,控制语句用于控制程序的执行流程,以实现程序的各种结构方式(C语言支持三种结构:顺序结构、选择结构、循环结构),它们由特定的语句定义符组成,C语言有九种控制语句,如下。
2.分支语句
- 条件判断语句:if 语句、switch 语句;
- 循环执行语句:do while 语句、while 语句、for 语句;
- 转向语句:break 语句、goto 语句、continue 语句、return 语句。
分支语句又称条件判断语句,通俗来讲,就是选择,如果要写一段程序,判断一个人的年龄,如果年龄大于等于18岁,则在控制台中打印"成年",那么我们只需要使用if语句即可完成这个程序,请看代码,这就是单分支语句。
#define _CRT_SECURE_NO_WARNINGS 1 #includeint main() { int age = 0; scanf("%d", &age); //单分支 if (age >= 18) printf("成年n"); return 0; }
那如果加一个需求,假设年轻不到18岁,打印"未成年",这时候就要用到 else 关键字了,与 if 配对,即如果 if 括号中的条件不满足,那么程序则会走 else 后面的语句,请看代码。这就是多分支语句。
#define _CRT_SECURE_NO_WARNINGS 1 #includeint main() { int age = 0; scanf("%d", &age); //多分支 if (age >= 18) printf("成年n"); else printf("未成年n"); return 0; }
如果再加些需求呢?假设年龄小于18,打印少年,年龄在大于等于18且小于30打印青年,年龄大于等于30且小于50打印中年,年龄大于等于50打印老年,那么这就用到另一种多分支了,请看下面这种多分支,即程序由上到下进行判断,满足哪个表达式,就执行对应后面的代码,如果都不满足,则执行else。
if(表达式1)
语句1;
else if(表达式2)
语句2;
else
语句3;
有了这种多分支,上面的需求就很好写了,请看如下代码。
#define _CRT_SECURE_NO_WARNINGS 1 #includeint main() { int age = 0; scanf("%d", &age); //多分支 if (age < 18) printf("少年n"); else if (age < 30) printf("青年n"); else if (age < 50) printf("中年n"); else printf("老年n"); return 0; }
补充1:例如判断年龄大于等于18且小于30那个表达式,为什么表达式中写的是age < 30,而不是age >= 18 && age < 30呢,原因是程序如果执行到这里,那么前面age < 18是一定不满足的,所以可以这样简写。
补充2:if、else、else if后面理论上是跟{ }的,这样写逻辑表述的更清晰,但程序中,如果在这些分支中只执行一个"逻辑",那么就可以不用跟大括号,也就是说,如果不写大括号,这些分支只控制一个逻辑,也就是广义上的"一条语句"。
下面介绍悬挂else,首先请看以下代码。
#includeint main() { int a = 0; int b = 2; if (a == 1) if (b == 2) printf("hehen"); else printf("hahan"); return 0; }
这个代码会执行什么呢?可能会有初学者朋友会进行这样的分析:a == 0,所以不走第一个 if 语句,那么就走下面的 else 语句,所以输出的结果是"haha",但我们实际运行一下,发现程序什么都不输出,原因是这个 else 并没有与if (a == 1) 配对,没有大括号的控制,实际上是与离它最近的 if 进行配对,所以程序根本不会进最外面的 if 语句中,故不会输出。所以合理的使用大括号,能够使得我们的代码逻辑更加清晰,请看修改后的代码,有了大括号的控制后,再也不会产生这种歧义了。
#includeint main() { int a = 0; int b = 2; if (a == 1){ if (b == 2){ printf("hehen"); } }else{ printf("hahan"); } return 0; }
这里还要说一下,关于判断语句中的代码风格问题,请看下面的代码,代码的本意是当 num 的值等于5的时候,则在屏幕上打印"haha",所以这段代码我不希望它进行打印,但是如果在判断语句中,把 == 不小心写成 = ,整个代码逻辑就会有问题,num 的值会发生改变(被赋值),而C语言中,只要是非0的数,都为真,所以程序会进行打印。
#includeint main() { int num = 1; if (num = 5) { printf("hahan"); } return 0; }
那怎么解决这种问题呢?我们只需要有一个良好的代码风格,请看下面代码,只需要在判断中写成 5 == num 这种风格,那么问题就得到解决了,如果不小心少写一个 = ,编译器会报错,而不会出现程序逻辑上的错误。
#includeint main() { int num = 1; if (5 == num) { printf("hahan"); //haha } return 0; }
下面请看两道简单的练习题。
练习1:判断一个整数是否为奇数。
这道题只需要对数字 n 进行取余判断,如果 n % 2 == 1,那一定就是奇数了,请看代码。
#includeint main() { int n = 17; if (n % 2 == 1) { printf("奇数n"); } else { printf("偶数n"); } return 0; }
练习2:输出1 - 100之间的奇数。
这题只需要对1~100之间的整数进行遍历(用之前介绍的 while 循环),判断每一个数是不是奇数,是的话就打印出来,不是的话就跳过。请看下面代码。
#includeint main() { int i = 1; while (i <= 100) { if (i % 2 == 1) { printf("%d ",i); } i++; } return 0; }
当然这题有一个取巧的办法,我们都知道奇数是什么,直接打印出来就行了。请看代码。
#includeint main() { int i = 1; while (i <= 100) { printf("%d ",i); i = i + 2; } return 0; }
下面介绍 switch 语句,它也是一种分支语句,常常用于多分支的情况,用于解决某些问题中使用 if else 比较复杂的情况,switch 括号中只能使用整型表达式,代码块内部还有 case break default 关键字,请看下面代码,case 决定入口,break 决定出口。这段代码是根据输入的 day 的整型值,来对照 case 后面的整型值来选择入口,程序从入口中一直执行,直到遇到 break 关键字终止整个 switch 语句。
#define _CRT_SECURE_NO_WARNINGS 1 #includeint main() { int day = 0; scanf("%d",&day); 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; }
如果需求发生改变,输入1、2、3、4、5就会打印工作日,输入6、7会打印休息日,那这个代码要怎么改呢?当然,第一个方法就是把所有输出的文字改一下,但这样会显得代码十分冗余,我们可以利用break这个终止的特性,来设计代码,请看如下代码。如果输入的数字是1~5之间,程序会找到 case 对应的入口,一直走下去,直到遇到break,这种现象,有些人称为"case穿透",注意,最后一个 case 中最好加上 break 关键字,原因是如果某一时刻需要继续往下写 case,如果不加 break 可能会有一些程序逻辑上的错误。
#define _CRT_SECURE_NO_WARNINGS 1 #includeint main() { int day = 0; scanf("%d", &day); switch (day) { case 1: case 2: case 3: case 4: case 5: printf("工作日n"); break; case 6: case 7: printf("休息日n"); break; } //最后一个break最好不要省略 return 0; }
下面介绍一下 default 关键字,如果输入的值在 case 后面都没有,那么就会走 default 语句,请看代码。当然,default 可以放在任意位置,一般默认在最后一个位置。
#define _CRT_SECURE_NO_WARNINGS 1 #includeint main() { int day = 0; scanf("%d", &day); switch (day) { case 1: case 2: case 3: case 4: case 5: printf("工作日n"); break; case 6: case 7: printf("休息日n"); break; default: printf("搞错了n"); break; //也加break } return 0; }
下面请看一道练习题,这段代码输出什么呢?
#includeint 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 = %dn", m, n); return 0; }
解释: 首先n的值为1,进入 case 1 ,进行穿透,到 case 3 之前n的值为2,m的值为3,然后进入 case 3 选择里面的 case 2,此时n的值为3,m的值为4,紧接着走 case 4(注意case 2中的break仅作用于case 2),最后n的值为3,m的值为5。
3.循环语句生活中有时候需要同一件事做好多次,那么在C语言中,就可以使用循环来做这些事。
关于while循环,前面已经有所介绍,请直接看下面的代码。while循环首先检查判断语句是否为真,如果为真,则执行循环体内容,然后再一次检查是否为真,为真则执行循环体内容,知道判断语句为假为止,那么while循环结束。
#include//打印1-10 int main() { int i = 1; while (i <= 10) { printf("%d ",i); ++i; } return 0; }
下面介绍break关键字,break作用是跳出循环,即结束循环,请看下面代码,代码依次打印1,2,3,4,到 i 的值为5的时候,终止循环,此时还没来得及打印,故程序输出结果为1 2 3 4。
#include//break终止整个循环 int main() { int i = 1; while (i <= 10) { if (i == 5) break; printf("%d ",i); ++i; } return 0; }
下面介绍continue关键字,continue作用是跳过本次循环,继续执行下一次循环/循环条件判断,请看下面代码。这段代码依次打印1,2,3,4,此时 i 的值为5,进入分支语句,遇到continue,跳过此次循环继续执行下一次循环,但此时 i 的值一直没变(还是5),所以还是会跳过此次循环,一直死循环执行下去。所以代码最后执行结果是1,2,3,4…
#include//continue 跳过本次循环 执行下一次循环 int main() { int i = 1; while (i <= 10) { if (i == 5) continue; printf("%d ", i); //1 2 3 4 死循环 ++i; } return 0; }
下面介绍C语言中 getchar() 函数,它是用来读取一个字符,返回值是读到的这个字符,遇到错误返回EOF(scanf 返回值是成功读到的个数,错误的时候也返回EOF),EOF本质上是-1,所以我们用 int 类型变量来接收 getchar() 函数更加适配。请看下面代码,其中 putchar() 是在控制台打印一个字符,那么此代码的意思是循环接收输入的字符,输入什么,输出什么,直到遇到EOF,而在控制台上使用 Ctrl + Z 就可以表示EOF了。
#includeint main() { int ch = 0; while ((ch = getchar()) != EOF) { putchar(ch); } }
程序运行如下。
下面看一段模拟用户输入密码的程序,请看代码,本意是想输入密码后,用户输入Y/N来确认密码,但实际上这个代码是有问题的。
#define _CRT_SECURE_NO_WARNINGS 1 #includeint main() { char password[20] = { 0 }; printf("请输入密码:"); scanf("%s", password); printf("请确认密码(Y/N):"); int ch = getchar(); if (ch == 'Y') { printf("确认成功n"); } else { printf("确认失败n"); } return 0; }
请看运行结果,我们在输入完密码后,控制台直接显示"确认失败"了,其本质的原因是因为scanf与getchar函数会在标准输入缓冲区中读取数据,我们输入的其实是 123456n ,这些字符进入缓冲区,scanf读取123456,最后getchar直接读取缓冲区里的n,所以会直接显示"确认失败"。
现在我们想到一个解决办法了,就是在使用getchar之前,把缓冲区的数据清除即可,我们可以使用getchar函数不断的读取缓冲区中的数据,直到遇到n。请看下面代码,循环结束后,n 也被读走了。这里循环读取数据(而不是使用一次getchar)的原因是因为防止第一次输入时有别的数据,例如用户输入"123456 123",scanf只会读到"123456"(只有 %c 占位符能读取到空格字符),缓冲区会有" 123n"数据。
#define _CRT_SECURE_NO_WARNINGS 1 #includeint main() { char password[20] = { 0 }; printf("请输入密码:"); scanf("%s", password); int temp = 0; while ((temp = getchar()) != 'n') { } printf("请确认密码(Y/N):"); int ch = getchar(); if (ch == 'Y') { printf("确认成功n"); } else { printf("确认失败n"); } return 0; }
下面介绍 for 循环,我认为 for 循环是C语言中最重要的循环,也是用的最多的循环,请看语法。
for(表达式1; 表达式2; 表达式3)
循环语句;
表达式1为初始化部分,用于初始化循环变量的。
表达式2为条件判断部分,用于判断循环时候终止。
表达式3为调整部分,用于循环条件的调整。
首先循环进行变量的初始化,然后进行条件判断,如果为真则进入循环体,一次循环结束后如果变量的调整部分,随后再执行条件判断部分…
通俗来讲,如果用数字4来表示执行循环体内容,那么for循环是这样执行的1243243243…2432。
请看下面的代码,打印1~10的数字,用下面的代码再来理解一下 for 循环。
#includeint main() { int i = 0; for (i = 1; i <= 10; i++) { printf("%d ",i); } return 0; }
下面我们研究一下break和continue关键字在 for 循环中的使用。
请看第一段代码,这段代码很明显输出1 2 3 4,与while循环类似。
#includeint main() { int i = 0; for (i = 1; i <= 10; i++) { if (5 == i) break; printf("%d ", i); } return 0; }
请看第二段代码,这段代码就与while不太一样了,continue跳出本次循环,但循环条件变量的值下一次会发生变化,所以输出为1 2 3 4 6 7 8 9 10。注意,最好不要在循环内部改变循环条件。
#includeint main() { int i = 0; for (i = 1; i <= 10; i++) { if (5 == i) continue; printf("%d ", i); } return 0; }
下面介绍几个 for 循环例子。
1.死循环,for 循环中的初始化部分,判断部分,调整部分是可以省略的。
#includeint main() { for (;;){ printf("hehen"); } }
2.不在 for 循环内部进行初始化,这段代码打印 0 1 2 3 4,原因是第二次外层循环的时候,内层循环的 j 没有初始化,故不再执行。所以一般不建议省略 for 循环的初始化。
#includeint main() { int i = 0; int j = 0; for (; i < 5; i++) { for (; j < 5; j++) { printf("%dn",j); } } return 0; }
3.使用多于一个循环变量控制也是可以的,请看代码。
#includeint main() { int x, y; for (x = 0, y = 0; x < 2 && y < 5; ++x, y++) { printf("hehen"); } return 0; }
下面请看一道题,求程序循环多少次?答案是 0 次,因为在判断语句中为 k = 0,进行赋值了,在C语言中 0 为假,所以程序不执行。
#includeint main() { int i = 0; int k = 0; for (i = 0, k = 0; k = 0; i++, k++) k++; return 0; }
下面介绍 do while 循环,do while 与 while 相似,不同的是 do while 循环至少执行一次,使用的场景有限,所以不是经常使用。
请看使用 do while 循环打印1~10的数字。程序不管判断语句,直接先执行一次,至于其他的方面,与 while 循环非常相似。
#include4.练习题int main() { int i = 1; do { printf("%d ",i); i++; } while (i <= 10); return 0; }
1.计算n的阶乘
解释: 使用循环依次从n乘到1。
#includeint main() { int n = 6;//数字 int res = 1; while (n >= 1) { res = res * n; n--; } printf("%dn",res); return 0; }
2.计算 1!+2!+3!+……+10!
解释: 第一种方法是传统遍历每一个数字,然后对每一个数字求阶乘,最后加到 sum 上。
#includeint main() { int sum = 0; for (int i = 1; i <= 10; i++) { int res = 1; for (int j = 1; j <= i; j++) { res *= j; } sum = sum + res; } printf("%dn",sum); return 0; }
解释: 第二种方法利用阶乘的特性,比如5!= 5 * 4!,那么我们只需要把上一个数的阶乘的值保存下来,用当前的数字乘上一个数的阶乘值,即是当前数字的阶乘值,最后用 sum 保存下来。
#includeint main() { int sum = 0; int res = 1; for (int i = 1; i <= 10; i++) { res = res * i; sum = sum + res; } printf("%dn",sum); return 0; }
3.求两个整数最大公约数
解释: 使用数学中的辗转相除法。以除数和余数反复做除法运算,当余数为 0 时,取当前算式除数为最大公约数。
#includeint main() { int a = 35; int b = 28; int tmp = 0; while (a % b != 0) { tmp = a % b; a = b; b = tmp; } printf("%dn", b);//7 return 0; }
4.打印100-200之间的素数
解释: 遍历100~200所有的数字,调用函数判断是否为素数,如果是,则打印出来。对于判断这个数是否为素数的方法,如果这个数能够被 [2, sqrt(i)] 之间的任意数整除,则这个数不是素数,原因是如果 m 能被 2 ~ m-1 之间任一整数整除,其二个因子必定有一个小于或等于sqrt(m),另一个大于或等于 sqrt(m)。
#include#include int isShuShu(int num) { for (int i = 2; i <= sqrt(num); i++) { if (num % i == 0) { return 0; } } return 1; } int main() { for (int i = 100; i <= 200; i++) { if (isShuShu(i)) { printf("%d ",i); } } return 0; }
5.编写代码,演示多个字符从两端移动,向中间汇聚
具体效果如下
H############!
He##########!!
Hel########!!!
Hell######!!!
Hello####d!!!
HelloW##ld!!!
HelloWorld!!!
解释: 本质定义两个字符转,使用循环改变字符串2中的字符,其中 Sleep(1000) 是将执行挂起一秒钟,system(“cls”) 是清空屏幕上所有内容。
#include#include #include #include int main() { char arr1[] = "HelloWorld!!!!"; char arr2[] = "##############"; int left = 0; int right = strlen(arr1) - 1; while (left <= right) { arr2[left] = arr1[left]; arr2[right] = arr1[right]; printf("%sn", arr2); Sleep(1000);//毫秒 #include system("cls"); //#include left++; right--; } printf("%sn", arr2); return 0; }
6.编写代码实现,模拟用户登录情景,并且只能输入三次密码
#define _CRT_SECURE_NO_WARNINGS 1 #include#include int main() { char psw[20] = { 0 }; int i = 0; for (i = 0; i < 3; i++) { printf("请输入密码:>"); scanf("%s",psw); if (strcmp(psw, "123456") == 0) { printf("登录成功n"); break; } else if(i < 2) { printf("密码错误,请重新输入n"); } } if (i == 3) { printf("密码三次错误,退出程序n"); } return 0; }
7.在一个有序数组中查找具体的某个数字n
解释: 使用二分查找,具体找的方式:找到数组的中间位置,检测中间位置的数据是否与要查找的数据 key 相等,如果相等,找到,打印下标,跳出循环,如果 key < arr[mid],则key可能在 arr[mid] 的左半侧,继续到左半侧进行二分查找,如果 key > arr[mid],则 key 可能在 arr[mid] 的右半侧,继续到右半侧进行二分查找,如果找到则返回下标,否则一直继续,直到区间中没有元素时,说明 key 不在集合中,打印"找不到"。
#includeint main() { //在一个有序数组中查找具体的某个数字n int arr[] = { 1,2,4,5,6,8,9,10,12,13 }; int k = 12; //需要寻找的元素 int left = 0; int right = sizeof(arr) / sizeof(arr[0]) - 1; //数组个数 while (left <= right) { int mid = (left + right) / 2; if (arr[mid] == k) { printf("找到了,下标为%dn",mid); break; } if (arr[mid] < k) { left = mid + 1; } else { right = mid - 1; } } if (left > right) { printf("找不到n"); } return 0; }
8.猜数字游戏
解释: 我们需要电脑生成一个随机数,所以使用 rand() 函数,其返回的数为 0 - 32767 之间的一个数字,所以我们对其 % 100 + 1,就可以得到1 - 100之间的数字了。但 rand() 每次生成的随机序列是一样的,需要在使用 rand() 之前调用 srand(传递一直在变化的值),对随机序列进行初始化,所以我们就将目前的时间戳传入到 srand() 里面,当然,srand() 不需要频繁调用,所以放在 main 函数即可。
#define _CRT_SECURE_NO_WARNINGS 1 #include5.goto语句#include #include void game() { int num = rand() % 100 + 1; //生成随机数 int res = 0; while (1) { printf("请猜一个数字->"); scanf("%d",&res); if (res == num) { printf("恭喜您 猜对了!!n"); break; } else if (res > num) { printf("猜大了n"); } else { printf("猜小了n"); } } } //菜单 void menu() { printf("***************n"); printf("***猜数字游戏***n"); printf("****1.paly*****n"); printf("****0.exit*****n"); printf("***************n"); } int main() { srand((unsigned int)time(NULL));//设定时间戳 int input = 0; 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; }
C语言中提供了可以随意滥用的 goto语句和标记跳转的标号。从理论上 goto语句是没有必要的,实践中没有goto语句也可以很容易的写出代码。但是某些场合下goto语句还是可以用的,例如一次跳出两层或多层循环。
下面代码是goto语句的一个简单介绍。again是自定义的一个标签,当代码由上至下执行到goto语句时,会跳转到again标签的位置,再次由上至下执行。
#includeint main() { again: printf("gon"); goto again; //跳到标签 不可以跨函数跳 }
下面写一个关机程序,如果用户输入"我是猪"则取消关机,请看代码。此程序执行后,电脑在120秒后就会关机,如果用户输入的字符串为"我是猪",那么电脑则取消关机,如果输入的不是"我是猪",那么程序执行goto语句,会再次让用户进行输入。
#define _CRT_SECURE_NO_WARNINGS 1 #include#include #include int main() { char ch[20] = {0}; go: system("shutdown -s -t 120"); printf("你的电脑即将关机,请输入 我是猪 取消关机n"); printf("请输入:"); scanf("%s",ch); if (strcmp(ch, "我是猪") == 0) { system("shutdown -a"); } else { goto go; } return; }
补充: C语言system() 函数是发出一个DOS命令,所谓DOS命令,就是电脑上按住 win + R,输入cmd 弹出来的黑色框框,可以在其中输入DOS命令行进行操作,例如输入命令 cd 文件名 即可转到此磁盘下对应文件名的文件夹,输入 dir 即可展示所有的目录。
在cmd窗口内输入 shutdown -s -t 120 则是表示电脑在120秒后关机命令,shutdown -a 表示电脑取消关机命令。
到这里本章分支与循环详解的内容全部结束了,下一篇文章将对函数进行详解,请朋友们多多点赞!~



