1. 什么是语句?
C语言中语句可分为以下 5 类:
- 表达式语句
- 函数调用语句
- 控制语句
- 复合语句
- 空语句
控制语句用于控制程序的执行流程,以实现程序的各种结构方式,它们由特定的语句定义符组成,C语言有 9 种控制语句。
可分为以下三类:
- 条件判断语句也叫分支语句:if 语句、switch 语句;
- 循环执行语句:do while 语句、while 语句、for 语句;
- 转向语句:break 语句、goto 语句、continue 语句、return 语句。
2. 分支语句(选择结构)
2.1 if 语句
作用:执行满足条件的语句
那 if 语句的语法结构是怎么样的呢?
语法结构:
单行格式:
if (表达式)
语句;
多行格式:
if (表达式)
语句1;
else
语句2;
多分支(多条件):
if (表达式1)
语句1;
else if (表达式2)
语句2;
else
语句3;
如果表达式结果为真,则语句执行。
在C语言中如何表示真假?
0表示假,非0表示真 。
如果条件成立,要执行多条语句,需要使用代码块。
if (表达式) {
语句列表1;
}
else {
语句列表2;
}
这里的一对 { } 就是一个代码块。
嵌套 if 语句:在 if 语句中,可以嵌套使用 if 语句,达到更精准的条件判断
2.1.1 悬空 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;
}
注意:else 的匹配是就近原则,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;
}
代码风格很重要,适当使用 { } 可以使代码的逻辑更加清楚。
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("hh");
}
//代码4
int num = 1;
if (5 == num) {
printf("hh");
}
代码2和代码4更好,逻辑更加清晰,不容易出错。
2.2 switch 语句
switch 语句常常用于执行多条件分支的情况。
语法结构:
switch (整型表达式)
{
case 整型常量表达式1:
语句1;
break;
case 整型常量表达式2:
语句2;
break;
case 整型常量表达式3:
语句3;
break;
default:
break;
}
注意:字符也是整型常量。
switch 语句特点:
- 缺点:判断的时候只能是整型(包括字符型),不可以是一个区间
- 优点:结构清晰,执行效率高
2.2.1 在 switch 语句中的 break
在 switch 语句中,我们没办法直接实现分支,搭配 break 使用才能实现真正的分支。如果 case 里没有 break,那么程序会一直向下执行。
break 语句 的实际效果是把语句列表划分为不同的分支部分。
编程好习惯:
在最后一个 case 语句的后面加上一条 break 语句。
(之所以这样写是可以避免出现在以前的最后一个 case 语句后面继续写case语句时忘记添加 break 语句)
2.2.2 default 子句
如果表达的值与所有的case标签的值都不匹配怎么办?
其实也没什么,结构就是所有的语句都被跳过而已。程序并不会终止,也不会报错,因为这种情况在C中并不认为时错误。
但是,如果你并不想忽略不匹配所有标签的表达式的值时该怎么办呢?
你可以在语句列表中增加一条 default 子句,把 default:写在任何一个case标签可以出现的位置。
当 switch 表达式的值并不匹配所有 case 标签的值时,这个 default 子句后面的语句就会执行。
所以,每个 switch 语句中只能出现一条 default 子句。
但是它可以出现在语句列表的任何一个位置,而且语句流会像执行一个 case 标签一样执行 default 子句。
编程好习惯:
在每个 switch 语句中都放一条 default 子句是一个好习惯,甚至可以在后面再加一个 break。
2.2.3 switch 允许嵌套使用
#include <stdio.h>
int main() {
int n = 1;
int m = 2;
switch (n) {
case 1:m++;
case 2:n++;
case 3:
switch (n) {
case 1:n++;
case 2:m++; n++;
break;
}
case 4:
m++;
break;
default:
break;
}
printf("m = %d, n = %d", m, n);
return 0;
}
3. 循环语句
- while
- for
- do while
3.1 while 循环
由于我们发现生活中很多的实际的例子是:同一件事情需要我们完成很多次。那我们怎么做呢?
C语言中给我们引入了:while 语句,可以实现循环。
作用:满足循环条件,执行循环语句
语法结构:
while (表达式)
循环语句;
解释:只要循环条件的结果为真,就执行循环语句。
比如我们实现:
在屏幕上打印1~10的数字。
#include <stdio.h>
int main() {
int i = 1;
while(i <= 10) {
printf("%d ", i);
i = i + 1;
}
return 0;
}
注意:在执行循环语句的时候,程序必须提供跳出循环的出口,否则出现死循环。
3.1.1 while 语句中的 break 和 continue
break 介绍
break 在 while 循环中的作用:
其实在循环中只要遇到了 break,就停止后期的所有的循环,直接终止循环。
所以:while 中的 break 是用于永久终止循环的。
#include <stdio.h>
int main() {
int i = 1;
while(i <= 10) {
if (5 == i) {
break;
}
printf("%d ", i);
i = i + 1;
}
return 0;
}
continue 介绍
continue 在 while 循环中的作用:
其实在循环中只要遇到了 continue,就会跳过当前循环的后续所有内容,直接去判断部分,进入下一次循环的判断。
所以:while 中的 continue 是用于跳过指定循环的。
#include <stdio.h>
int main() {
int i = 1;
while(i <= 10) {
i = i + 1;
if (5 == i) {
continue;
}
printf("%d ", i);
}
return 0;
}
注意:i = i + 1;先执行,会对后续代码的结果产生影响。
3.1.2 while 循环的使用
- 使用 while 循环可以用来清理缓冲区。
首先,介绍以下 getchar() 和 putchar() 函数的使用。
#include <stdio.h>
int main() {
int ch = 0;
while ((ch = getchar()) != EOF) {
putchar(ch);
//printf("%c", ch);
}
return 0;
}
- getchar():获取字符的 Ascii 码值。当读取错误或者读取到文件末尾时,返回EOF。
- putchar():以字符的形式打印 变量的值。
接下来,考虑一个读取密码的案例。
#include <stdio.h>
int main() {
char pw[20] = { 0 };
printf("请输入密码:");
scanf("%s", pw);
getchar();//读取了'\n'
printf("请确认密码(Y/N):>");
int ret = getchar();
if ('Y' == ret) {
printf("Yes\n");
}
else {
printf("No\n");
}
return 0;
}
注:
- 在输入 "abc",按下回车后 在输入缓冲区的内容为 "abc\n";
- scanf() 函数读到 空格/ '\n' 停止,并且只读取有效地字符串,会遗留 空格及后续字符/ '\n' 在输入缓冲区中。
改正:
#include <stdio.h>
int main() {
char pw[20] = { 0 };
printf("请输入密码:");
scanf("%s", pw);
int ch = 0;
while ((ch = getchar()) != '\n') {
;
}
printf("请确认密码(Y/N):>");
int ret = getchar();
if ('Y' == ret) {
printf("Yes\n");
}
else {
printf("No\n");
}
return 0;
}
- 使用 while 循环只打印数字字符,跳过其他字符。
#include <stdio.h>
int main() {
char ch = '\0';
while ((ch = getchar()) != EOF) {
if (ch < '0' || ch > '9') {
continue;
}
putchar(ch);
}
return 0;
}
3.2 for 循环
我们已经知道了 while 循环,但是我们为什么还要一个 for 循环呢?
作用:满足循环条件,执行循环语句
首先来看看 for 循环的语法:
3.2.1 语法
for (表达式1, 表达式2, 表达式3)
循环语句;
- 表达式1:初始化部分,用于初始化循环变量。
- 表达式2:条件判断部分,用于判断循环是否终止。
- 表达式3:调整部分,用于循环条件的调整。
实际问题
使用 for 循环 在屏幕上打印 1-10 的数字。
#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
我们发现在 for 循环中也可以出现 break 和 continue,他们的意义和在 while 循环中是一样的。
但是还是有些差异:
//代码1
#include <stdio.h>
int main() {
int i = 1;
while(i <= 10) {
if (5 == i) {
continue;
}
printf("%d ", i);
i = i + 1;
}
return 0;
}
//代码2
#include <stdio.h>
int main() {
for (int i = 1; i <= 10; i++) {
if (5 == i) {
continue;
}
printf("%d ", i);
}
return 0;
}
- 在 while 循环中,continue 会跳过本次循环的后续部分,直接进入下次循环的条件判断部分,可能略过了循环的条件调整部分造成死循环。
- 在 for 循环中,continue 会跳过本次循环的后续部分,直接进入下次循环的条件调整部分,再进入条件判断部分。
3.2.3 for 语句的循环控制变量
建议:
- 不可在 for 循环体内修改循环变量,防止 for 循环失去控制。
- 建议 for 语句的循环控制变量的取值采用“前闭后开区间”写法。
int i = 0;
//前闭后开的写法
for (i = 0; i < 10; i++) {
;
}
//两边都是闭区间
for (i = 0; i <= 9; i++) {
;
}
3.2.4 一些 for 循环的变种
- for 循环中的初始化部分,判断部分,调整部分是可以省略的。
for (;;) {
printf("hehe\n");
}
for 循环的判断部分省略意味着判断会恒成立。
- C99 语法才支持了在 for 循环的初始化部分 初始化循环变量,编译器不一定支持。(C++支持这种写法)
for (int i = 0; i < 10; i++) {
printf("hehe\n");
}
- 使用多个变量控制循环
int x, y;
for (x = 0, y = 0; x < 2 && y < 5; ++x, y++) {
printf("hehe\n");
}
- 使用嵌套的 for 循环
int i = 0;
int j = 0;
for (i = 0; i < 10; i++) {
for (j = 0; j < 10; j++) {
printf("hehe\n");
}
}
注意:嵌套 for 循环的初始化部分不要省略。
测试题:请问循环要循环多少次?
#include <stdio.h>
int main() {
int i = 0;
int k = 0;
for (i = 0, k = 0; k = 0; i++, k++) {
k++;
return 0;
}
赋值 k = 0; 0 为假,不进入循环。
3.3 do...while() 循环
作用:满足循环条件,执行循环语句
3.3.1 do语句的语法
do
循环语句;
while(表达式);
与 while 的区别在于 do...while 会先执行一次循环语句,在判断循环条件。
3.3.2 do语句的特点
循环至少执行一次,使用的场景有限,所以不是经常使用。
3.3.3 do 语句中的 break 和 continue
意义和在 while 循环中是一样的。
3.4 嵌套循环
作用:在循环体中再嵌套一层循环,解决一些实际问题
示例:打印一个正方形图案。
#include <stdio.h>
int main() {
int i = 0;
int n = 0;
scanf("%d", &n);
for (i = 0; i < n; i++) {
int j = 0;
for (j = 0; j < n; j++) {
printf("* ");
}
printf("\n");
}
return 0;
}
3.4 练习
- 计算 n 的阶乘。
- 计算
。
- 编写代码,演示多个字符从两端移动,向中间汇聚。
- 编写代码,模拟用户登录场景,只允许输入三次密码,如果密码正确则提示登陆成功,如果三次均输入错误,则退出程序。
练习参考代码:
//代码1
#include <stdio.h>
int main() {
int sum = 1;
int i = 1;
int n = 0;
scanf("%d", &n);
for (i = 1; i <= n; i++) {
sum *= i;
}
printf("%d\n", sum);
return 0;
}
//代码2
#include <stdio.h>
int main() {
int sum = 1;
int i = 1;
int ret = 0;
for (i = 1; i <= 10; i++) {
sum *= i;
ret += sum;
}
printf("%d\n", ret);
return 0;
}
//代码3
#include <stdio.h>
#include <windows.h>
#include <stdlib.h>
int main() {
char arr1[] = "welcome to abc!!!!";
char arr2[] = "##################";
int left = 0;
int right = strlen(arr1) - 1;
while (left <= right) {
arr2[left] = arr1[left];
arr2[right] = arr1[right];
printf("%s\n", arr2);
Sleep(1000);
//清空屏幕
system("cls");//system是一个库函数,可以执行系统命令
left++; right--;
}
return 0;
}
//代码4
#include <stdio.h>
#include <string.h>
int main() {
int i = 0;
char pw[20] = { 0 };
for (i = 0; i < 3; i++) {
printf("请输入密码:>");
scanf("%s", pw);
if (strcmp(pw, "abcdef") == 0) {
printf("登陆成功\n");
break;
} else {
printf("密码错误\n");
}
}
if (i == 3) {
printf("三次密码均输入错误,退出程序\n");
}
return 0;
}
Sleep():将进程挂起一段时间。在头文件 windows.h 中。
system():执行系统命令的库函数。在头文件 stdlib.h 中。
- 比较两个字符串是否相等,不能使用==,而应该使用一个库函数:strcmp。
- strcmp():如果返回值是 0,表示 2 个字符串相等。
4. goto 语句
作用:可以无条件跳转语句
C语言中提供了可以任意滥用的 goto 语句和标记跳转的标号。
语法结构
again:
代码块;
goto again;
解释:如果标记的名称存在,执行到 goto 语句时,会跳转到标记的位置。
goto 语句不能跨函数跳转。
从理论上 goto 语句是没有必要的,实践中没有 goto 语句也可以很容易的写出代码。
但是某些场合下 goto 语句还是用得着的,最常见的用法就是终止程序在某些深层嵌套的结构的处理过程。
例如:一次跳出两层或者多层循环。(多层循环这种情况使用 break 是达不到目的的。它只能从内层循环退出到上一层循环。)
goto 语句真正适合的场景如下:
for (...) {
for (...) {
for (...) {
if (disaster) {
goto error;
}
}
}
}
error:
if (disaster) {
//处理错误情况
}
下面是使用 goto 语句的一个例子,然后使用循环的实现方式替换 goto 语句:
一个关机程序:
#include <stdio.h>
int main() {
char input[10] = 0;
system("shutdown -s -t 60");
again:
printf("电脑将在 1 分钟内关机,如果输入:666,就取消关机!\n请输入:>");
scanf("%s", input);
if (0 == strcmp(input, "666")) {
system("shutdown -a");
}
else {
goto again;
}
return 0;
}
#include <stdio.h>
int main() {
char input[10] = 0;
system("shutdown -s -t 60");
while(1) {
printf("电脑将在 1 分钟内关机,如果输入:666,就取消关机!\n请输入:>");
scanf("%s", input);
if (0 == strcmp(input, "666")) {
system("shutdown -a");
break;
}
}
return 0;
}