分支和循环语句
目录
什么是语句?
分支语句(选择结构)
if语句
switch语句
循环语句
while
for
do while
练习
goto语句
什么是语句?
C语句可分为以下五类:
1. 表达式语句
2. 函数调用语句
3. 控制语句
4. 复合语句
5. 空语句
本文介绍的是控制语句,控制语句用于控制程序的执行流程,以实现程序的各种结构方式,它们由特定的语句定义符组成,C语言有九种控制语句
可分成以下三类:
1. 条件判断语句也叫分支语句:if语句、switch语句;
2. 循环执行语句:do while语句、while语句、for语句;
3. 转向语句:break语句、goto语句、continue语句、return语句。
分支语句(选择结构)
生活中常常会面临各种选择,我们经常会说,比如如果大学好好学习,就可以找到一份好工作,如果不好好学习,那毕业就等于失业,这就是典型的分支语句
if语句
if语句的语法结构:
单分支
if(表达式)语句;
#include <stdio.h>
int main()
{int age;scanf("%d", &age);if (age < 18){printf("未成年\n");}return 0;
}
双分支
if(表达式)语句1;
else语句2;
#include <stdio.h>
int main()
{int age;scanf("%d", &age);if (age < 18){printf("未成年\n");}else{printf("成年\n");}return 0;
}
多分支
if(表达式1) 语句1;
else if(表达式2)语句2;
else语句3; #include <stdio.h>
int main()
{int age;scanf("%d", &age);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;
}
注:
1.表达式为真,则语句执行;表达式为假,语句不执行
2.如果 { } 内只有一条语句,那么 { } 可以省略;如果有多条语句,则 { } 不能省略,建议都加上
悬空else问题:
#include <stdio.h>
int main()
{int a = 0;int b = 2;if (a == 1)if (b == 2)printf("hehe\n");elseprintf("haha\n");return 0;
}
我们乍一看认为程序输出haha,实际上什么也没输出,C语言中else的匹配机制是和离自己最近的未匹配的if进行匹配,所以代码风格很重要,如果想让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;
}
代码风格对比:
代码一:
#include <stdio.h>
int main()
{int num = 0;if (num == 3){printf("haha\n");}return 0;
}
代码二:
#include <stdio.h>
int main()
{int num = 0;if (3 == num){printf("haha\n");}return 0;
}
建议代码二,因为如果我们不小心把 == 写成了 =,采用代码二写法编译器会直接报错,方便检查
练习:判断一个数是不是奇数
#include <stdio.h>
int main()
{int x;scanf("%d", &x);if (x % 2 == 1){printf("奇数\n");}else{printf("偶数\n");}return 0;
}
switch语句
switch语句也是一种分支语句,常常用于多分支情况,比如:输入1,输出星期一;输入2,输出星期二...输入7,输出星期天,如果使用if语句,分支情况太多,比较麻烦,此时可以用switch语句
switch语句格式
switch(整型表达式)
{case 整型常量表达式:语句;
}
按照上述代码,我们写出的代码如下:
#include <stdio.h>
int main()
{int day;scanf("%d", &day);switch(day){case 1:printf("星期一\n");case 2:printf("星期二\n");case 3:printf("星期三\n");case 4:printf("星期四\n");case 5:printf("星期五\n");case 6:printf("星期六\n");case 7:printf("星期天\n");}return 0;
}
switch中的break语句
当我们输入5的时候,发现星期五、星期六、星期天都被打印出来了,这是为什么呢???
当我们选择从某个case进入后,如果没有遇到break,会一直执行到整个switch语句结束,如果遇到了break,就会提前跳出,因此正确代码应该是给每次打印完后加上break
#include <stdio.h>
int main()
{int day;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.输入1-5,输出"weekday"
2.输入6-7,输出"weekend"
代码中我们可以将多个 case 合并到一起,建议在最后一个 case 语句后也加上 break,之所以这么写是可以避免出现在以前的最后一个case语句后忘了添加break语句
#include <stdio.h>
int main()
{int day;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;
}
switch中的 default 语句
如果我们输入的值与case标签的值都不匹配怎么办?其实也没什么,结构就是所有的语句都被跳过而已。 程序并不会终止,也不会报错,因为这种情况在C中并不认为是个错误。 但是,如果你并不想忽略不匹配所有标签的表达式的值时该怎么办呢?
你可以在语句列表中增加一条default子句,把下面的标签 default: 写在任何一个 case 标签可以出现的位置。 当 switch 表达式的值并不匹配所有 case 标签的值时,这个 default 子句后面的语句就会执行。 所以,每个switch语句中只能出现一条default子句。 但是它可以出现在语句列表的任何位置,而且语句流会像执行一个case标签一样执行default子句。
一般建议将 default 子句放在所有case之后,并且也加上一个break
#include <stdio.h>
int main()
{int day;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("输入错误!!!\n");break;}return 0;
}
练习:
#include <stdio.h>
int main()
{int n = 1;int m = 2;switch (n){case 1:m++; //3case 2:n++; //2case 3:switch (n) //switch允许嵌套使用{case 1:n++;case 2:m++; //4n++; //3break;}case 4:m++; //5break;default:break;} printf("m = %d, n = %d\n", m, n); //m = 5, n = 3return 0;
}
注: switch语句嵌套时,break只能跳出当前的switch语句
循环语句
生活中我们经常要重复做一件事情,比如学习计算机,每天都要敲代码,此时就要用到循环语句
while
while语法结构
while(表达式)循环语句;
比如打印1~10
#include <stdio.h>
int main()
{int i = 1;while (i <= 10){printf("%d ", i);i++;}return 0;
}
while中的break与continue
break的作用是跳出整个循环,下面代码打印 1 2 3 4
#include <stdio.h>
int main()
{int i = 1;while (i <= 10){if (i == 5)break;printf("%d ", i);i = i + 1;}return 0;
}
continue的作用是跳出本轮循环,不执行cotinue后的代码,直接进入下一次循环,下面代码打印 1 2 3 4 然后就进入了死循环
#include <stdio.h>
int main()
{int i = 1;while (i <= 10){if (i == 5)continue;printf("%d ", i);i = i + 1;}return 0;
}
下面代码打印 2 3 4 6 7 8 9 10 11
#include <stdio.h>
int main()
{int i = 1;while (i <= 10){i = i + 1;if (i == 5)continue;printf("%d ", i);}return 0;
}
再看两段代码:
#include <stdio.h>
int main()
{int ch = 0;while ((ch = getchar()) != EOF)putchar(ch);return 0;
}
getchar用于读取键盘输入的字符,返回该字符的ASCII值,当读取到文件结尾结尾或者读取错误时返回EOF,EOF本质是个宏,值为-1,putchar函数传入字符的ASCII值,可以打印该字符
#include <stdio.h>
int main()
{char ch = '\0';while ((ch = getchar()) != EOF){if (ch < '0' || ch > '9')continue;putchar(ch);}return 0;
}
上面代码可以跳过所有的非数字字符,只打印数字字符
for
for循环语法格
for(表达式1; 表达式2; 表达式3)循环语句;
表达式1为初始化部分,用于初始化循环变量
表达式2为条件判断部分,用于判断循环时候终止。
表达式3为调整部分,用于循环条件的调整。
对比while循环可以发现,while循环也是有这三部分的,但可能这三部分距离比较远,这样查找修改就不够集中和方便,所以for循环的风格更胜一筹,for循环使用的频率也更高
eg:在屏幕上打印1-10
#include <stdio.h>
int main()
{for (int i = 1; i <= 10; i++){printf("%d ", i);}return 0;
} for循环中的break与continue
for循环中的break与continue,他们两个在for循环中的作用和在while循环中作用是完全一样的,但由于for循环代码风格原因,使用continue时结果可能略有不同
#include <stdio.h>
int main()
{int i = 0;for (i = 1; i <= 10; i++){if (i == 5)continue;printf("%d ", i);}return 0;
} 上面代码打印 1 2 3 4 6 7 8 9 10
#include <stdio.h>
int main()
{int i = 0;for (i = 1; i <= 10; ){if (i == 5)continue;printf("%d ", i);i++;}return 0;
} for循环的三个表达式不是必须写到一起的,上面代码打印 1 2 3 4,然后死循环
一些for循环的变种
代码一: for循环的三部分都是可以省略的,但不建议,容易导致问题
#include <stdio.h>
int main()
{for (;;){printf("hehe\n");}return 0;
}
代码二: 嵌套for循环,最终打印9个hehe
#include <stdio.h>
int main()
{int i = 0;int j = 0;//这里打印多少个hehe?for (i = 0; i < 3; i++){for (j = 0; j < 3; j++){printf("hehe\n");}}return 0;
}
代码三: 如果省略掉初始化部分,外层循环循环一次后,j 就会加到3,外层循环的第二次和第三次都不会再进入内层循环的循环体了,最后打印 3个 hehe
#include <stdio.h>
int main()
{int i = 0;int j = 0;for (; i < 3; i++){for (; j < 3; j++){printf("hehe\n");}}return 0;
}
代码四:使用多余一个变量,控制循环,打印2个hehe
#include <stdio.h>
int main()
{int x, y;for (x = 0, y = 0; x < 2 && y < 5; ++x, y++){printf("hehe\n");}return 0;
}
一道笔试题:
请问循环要循环多少次? 注意判断部分,是赋值符号,因此循环0次
#include <stdio.h>
int main()
{int i = 0;int k = 0;for (i = 0, k = 0; k = 0; i++, k++)k++;return 0;
}
do while
语法格式
do循环语句;
while(表达式);
先执行一次循环语句,然后判断表达式是否成立,决定是否继续循环,因此do while语句的特点是循环体至少执行一次,使用的场景有限,不是很常用
eg: 打印1~10
#include <stdio.h>
int main()
{int i = 1;do{printf("%d ", i);i++;} while (i <= 10);return 0;
}
练习
1. 计算 n 的阶乘
#include <stdio.h>
int main()
{int n; scanf("%d", &n);int ret = 1;for (int i = 1; i <= n; i++)ret = ret * i;printf("%d\n", ret);return 0;
}
2.计算 1!+2!+ ... + n!
#include <stdio.h>
int main()
{int n; scanf("%d", &n);int ret = 0, tmp = 1;for (int i = 1; i <= n; i++){tmp *= i;ret += tmp;}printf("%d\n", ret);return 0;
}
3. 在一个有序数组中查找具体的某个数字n(二分查找)
#include <stdio.h>
int main()
{int a[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };int x;scanf("%d", &x);int l = 0, r = sizeof(a) / sizeof(int) - 1;int mid = 0;while (l <= r){mid = (l + r) / 2;if (x > a[mid]) l = mid + 1;else if (x < a[mid]) r = mid - 1;else{break;}}if(l <= r) printf("找到了, 下标为%d\n", mid);elseprintf("查找的值不存在!\n");return 0;
}
4. 编写代码,演示多个字符从两端移动,向中间汇聚
#include <stdio.h>
#include <windows.h>
#include <string.h>
int main()
{char arr1[] = "welcome to computer world!";char arr2[] = "##########################";int l = 0, r = strlen(arr1) - 1;while (l < r){arr2[l] = arr1[l];arr2[r] = arr1[r];l++, r--;printf("%s\n", arr2);Sleep(1000);}return 0;
}
5. 编写代码实现,模拟用户登录情景,并且只能登录三次。(只允许输入三次密码,如果密码正确则提示登录成功,如果三次均输入错误,则退出程序)
#include <stdio.h>
#include <string.h>
int main()
{char arr[20];int i = 0;for (;i < 3; i++){scanf("%s", arr);if (strcmp(arr, "password") == 0) //strcmp返回值为0, 代表字符串相等{break;}}if (i == 3)printf("三次密码均错误, 登录失败!\n");elseprintf("登录成功!\n");return 0;
}
6.猜数字游戏
#include <stdio.h>
#include <stdlib.h>void menu()
{printf("#############################\n");printf("######### 0 退出游戏 #########\n");printf("######### 1 开始游戏 #########\n");printf("#############################\n");
}void game()
{int input = 0;int random = rand() % 100;while (1){printf("请输入要猜的数字: \n");scanf("%d", &input);if (input > random)printf("猜大了\n");else if (input < random)printf("猜小了\n");else{printf("恭喜你, 猜中了!\n");break;}}
}int main()
{int input = 0;do{menu();printf("请选择:->\n");scanf("%d", &input);switch (input){case 1:game();break;case 0:printf("退出游戏\n");break;default:printf("选择错误, 请重新选择\n");break;}} while (input);return 0;
}
goto语句
C语言中提供了可以随意滥用的 goto语句和标记跳转的标号。从理论上 goto语句是没有必要的,实践中没有goto语句也可以很容易的写出代码。但是某些场合下goto语句还是用得着的,最常见的用法就是终止程序在某些深度嵌套的结构的处理过程。例如:一次跳出两层或多层循环。多层循环这种情况使用break是达不到目的的。它只能从最内层循环退出到上一层的循环。
for(...)for(...){for(...){if(disaster)goto error;}}…
error:if(disaster)// 处理错误情况
下面使用 goto 语句 实现了一个关机程序:
#include <stdio.h>
int main()
{char input[10] = { 0 };system("shutdown -s -t 60");
again:printf("电脑将在1分钟内关机,如果输入:我是猪,就取消关机!\n请输入:>");scanf("%s", input);if(0 == strcmp(input, "我是猪")){printf("取消关机!");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;
}
