2. 分支语句 (选择结构)
2.1 if语句
if语句的语法结构如下:
if (表达式)
语句1; // 如果表达式的结果为真, 则语句执行.
if (表达式)
语句1;
else
语句2;
// 多分支
if (表达式1)
语句1;
else if (表达式2)
语句2;
else
语句3;
C语言中, 0表示假, 非0表示真.
来看下面的代码演示如何使用if语句:
#include <stdio.h>
int main()
{
// if 语句的基本写法
if (3 == 5)
printf("hehe\n"); // 什么都不会打印因为 if 条件表达式 为假
if (3 == 3)
printf("hehe\n"); // if 条件表达式 为真, 打印 hehe
int a = 10;
if (a = 3)
printf("hehe\n"); // 3 为非0值, 为真, 打印 hehe (这种写法不常用)
int b = 10;
if (b == 3)
printf("hehe\n"); // if条件表达式 为假, 不打印
// 可以表达具有某种意义的逻辑, 比如年龄
int age1 = 20;
if (age1 > 18)
printf("成年\n"); // 打印成年
// if 语句的第二种写法. 注意 if 或 else 要跟多条语句时必须加 {}, 即使用代码块
int age2 = 20;
//int age2 = 16;
if (age2 < 18)
{
printf("未成年\n");
printf("不能饮酒\n");
}
else
{
printf("成年\n");
}
// if 多分支写法. 多分支只会执行一个分支
int age3 = 10;
scanf("%d", &age3);
if (age3 < 18)
printf("青少年\n");
else if (age3 >= 18 && age3 < 28) // 不能多个比较运算符连续(18<=age<28不允许这种写法)
printf("青年\n");
else if (age3 >= 28 && age3 < 40)
printf("中年\n");
else if (age3 >= 40 && age3 < 60)
printf("壮年\n");
else if (age3 >= 60 && age3 <= 100)
printf("老年\n");
else
printf("老寿星\n");
// else 有多条语句也需要使用代码块
int age4 = 20;
if (age4 < 18)
printf("未成年\n");
else
{
printf("成年\n");
printf("打游戏\n");
}
return 0;
}
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(haha)和if(a==1)是匹配的, 但是当讲上面的代码复制到编译器了之后, 编译器运行后就会把else(haha)和if(b==2)匹配到一起. 所以运行结果就会什么都没有打印.
那么这里的原因就是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("hehe\n");
}
//代码4
int num = 1;
if (5 == num)
{
printf("hehe\n");
}
由上述代码可以看出, 代码2和代码4更好, 逻辑更加清晰, 不容易出错.
2.1.3 练习
1.判断一个数是否为奇数
int main()
{
//判断一个数是否为奇数
int num = 0;
scanf("%d", &num);
//判断
if (num % 2 == 1)
printf("奇数\n");
else
printf("No\n");
return 0;
}
2.输出1-100之间的奇数
int main()
{
int i = 1;
while (i <= 100)
{
if (i % 2 == 1)
printf("%d ", i);
i++;
}
return 0;
}
int main()
{
int i = 1;
while (i <= 100)
{
printf("%d ", i);
i += 2;
}
return 0;
}
2.2 switch语句
switch语句也是一种分支语句, 常常用于多分支的情况。
int main()
{
int day = 0;
scanf("%d", &day);
//if (1 == day)
// printf("星期1\n");
//else if (2 == day)
// printf("星期2\n");
//else if(3 == day)
// printf("星期3\n");
//else ....
int n = 1;
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;
}
比如:
输入1,输出星期一
输入2,输出星期二
输入3,输出星期三
输入4,输出星期四
输入5,输出星期五
输入6,输出星期六
输入7,输出星期日
我们如果写成 if...else if...else if 的形式太复杂,那么我们就得有不一样的语法形式. 这就是switch 语句.
switch(整型表达式)
{
语句项;
}
语句项即case语句:
case 整型常量表达式:
语句;
2.2.1 switch的使用
int main()
{
int day = 0;
scanf("%d", &day);
switch (day)
{
case1:
printf("星期一\n");
case2:
printf("星期二\n");
case3:
printf("星期三\n");
case4:
printf("星期四\n");
case5:
printf("星期五\n");
case6:
printf("星期六\n");
case7:
printf("星期七\n");
}
return 0;
}
预期: 输入一个数字, 打印对应的星期几.
可以看到, 程序是由3进入打印星期三, 但是后面又打印了星期四到星期天. 那么这里需要注意, case只负责入口, 没有负责出口, 所以进入之后会往下执行所有的case.
在switch语句中,我们没办法直接实现分支,搭配break使用才能实现真正的分支.
我们可以使用break语句控制switch语句的中断.
加上break之后, 可以看到按照预期结果执行了.
此时我们更改需求:
1.输入1-5,输出的是“weekday”
2.输入6-7,输出"weekend"
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;
}
break语句 的实际效果是 把语句列表划分为不同的分支部分
2.2.2 default字句
当前面的程序中, 输入的不是1~7范围内的数字的时候, 所有分支就会被跳过, 那么如果此时并不想忽略不匹配所有标签的表达式的值时, 就可以使用default字句, 写在任何一个 case 标签可以出现的位置.
当 switch 表达式的值并不匹配所有 case 标签的值时,这个default 子句后面的语句就会执行.
但是它可以出现在语句列表的任何位置,而且语句流会像执行一个case标签一样执行default子句。
// 在上面代码的最后加上
default:
printf("选择错误\n");
break;
编程好习惯
在每个 switch语句中都放一条 default子句是个好习惯,甚至可以在后边再加一个 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;
}
执行结果:
让我们逐步分析代码的执行过程:
初始化整数变量 n 为1,整数变量 m 为2。
进入 switch (n),n 的值为1,因此进入 case 1 分支。
在 case 1 中,m 的值增加到3,但没有 break 语句,所以继续执行下一个 case。
进入 case 2,n 的值增加到2,同样没有 break 语句。
接下来,进入 case 3,此处有一个嵌套的 switch 语句。
进入内部的 switch (n),此时 n 的值是2。
在内部的 switch 中,进入 case 2,n 的值增加到3,然后执行 m++,m 的值增加到4,接着 n++,n 的值增加到4。
内部的 switch 没有 break 语句,所以控制流继续流向外部的 case 4。
在外部的 case 4 中,m 的值增加到5。
switch 结束,没有 default 分支被执行。
最后,printf 打印出 m 和 n 的值,即 m = 5, n = 3。
所以,代码的执行结果是 m = 5, n = 3。
3. 循环语句
循环语句有以下三种.
while
for
do while
3.1 while循环
我们已经掌握了if语句:
if (表达式)
语句1; // 如果表达式的结果为真, 则语句执行.
当条件满足的情况下,if语句后的语句执行,否则不执行
但是这个语句只会执行一次。
由于我们发现生活中很多的实际的例子是: 同一件事情我们需要完成很多次, 那我们怎么做呢?
C语言中给我们引入了: while 语句,可以实现循环
//while 语法结构
while(表达式)
循环语句;
while语句执行的流程:
//打印1~10数字
int main()
{
int i = 1;
while (i <= 10)
{
printf("%d ", i);
i++;
}
return 0;
}
3.1.1 while语句中的break和continue
break介绍
int main()
{
int i = 1;
while (i <= 10)
{
if (5 == i)
break;
printf("%d ", i);
i++;
}
return 0;
}
总结:
break在while循环中的作用:
其实在循环中只要遇到break,就停止后期的所有的循环,直接终止循环.
所以: while中的break是用于 永久终止循环的
continue介绍
int main()
{
int i = 1;
while (i <= 10)
{
i++;
if (5 == i)
continue;
printf("%d ", i);
}
return 0;
}
总结:
continue在while循环中的作用就是:
continue是用于终止本次循环的,也就是本次循环中continue后边的代码不会再执行,
而是直接跳转到while语句的判断部分。进行下一次循环的入口判断
再来看以下代码:
int main()
{
//int ch = 0;
//while(getchar())
int ch = getchar();
printf("%c\n", ch); //1
putchar(ch); //2
return 0;
}
getchar() 是一个标准库函数,它用于从标准输入流(通常是键盘)获取一个字符。
当程序执行 getchar() 时,它会等待用户在键盘上输入一个字符,并且该字符会存储在输入缓冲区中。
输入缓冲区是一个临时存储区域,用于存储等待处理的输入数据。当用户按下回车键时,输入缓冲区中的字符会被传递给 getchar() 函数。
如果输入缓冲区为空(没有待处理字符), getchar() 会等待用户输入,直到一个字符被键入。
如果用户输入了文件结束符(通常是Ctrl+D或Ctrl+Z,具体取决于操作系统),则 getchar() 返回EOF,这会导致循环终止,程序结束执行。
putchar() 打印字符.
为什么 getchar()使用 int接收字符.
EOF(End of File): getchar() 通常返回一个整数,而不是 char,是为了能够区分正常字符和文件结束符(EOF)。EOF是一个表示输入流结束的特殊值,通常被定义为一个负数,如常见的 -1。如果使用 char 来接收字符,就无法清晰地表示EOF。
如图输入C, 即打印出C.
int main()
{
int ch = 0;
while ((ch = getchar()) != EOF)
{
putchar(ch);
}
return 0;
}
上述代码中:
假设用户在程序运行时按下键盘上的一些字符:
1. 用户按下键盘上的字母 'A',然后按回车键。此时,输入缓冲区包含字符 'A' 和回车字符 '\n'。
2. 程序执行 getchar(),它从输入缓冲区读取字符 'A' 并将其存储在变量 ch 中。然后, putchar(ch) 输出 'A' 到屏幕上。
3. 接着,程序再次执行 getchar(),这次它从输入缓冲区读取回车字符 '\n' 并将其存储在变量 ch 中。然后, putchar(ch) 输出回车字符 '\n' 到屏幕上。
4. 循环继续,程序等待用户继续输入。
如果用户继续输入字符,重复上述步骤,输入缓冲区将随之变化。例如,如果用户依次输入 'B'、'C'、回车,那么输入缓冲区将包含字符 'B'、'C' 和回车字符 '\n'。
这个过程会一直持续,直到用户输入文件结束符(EOF),在大多数系统上通常是Ctrl+D(或Ctrl+Z)。当用户执行文件结束符时, getchar() 会返回EOF,循环终止,程序结束执行。
int main()
{
//举一个例子
//假设密码是一个字符串
char password[20] = { 0 };
printf("请输入密码:>");
scanf("%s", password);//123456
//getchar();//读取了\n
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;
}
这段代码的目的是获取用户输入的密码并进行确认。它使用了 scanf() 获取密码,并使用 getchar() 清空输入缓冲区(消耗掉回车字符 '\n'),然后询问用户是否要确认密码。根据用户的选择输出 "Yes" 或 "No"。
让我通过示例说明代码的执行过程:
1. 用户被提示输入密码,输入 "123456" 并按下回车键。
2. scanf("%s", password); 读取用户输入的密码,并将其存储在 password 数组中。
3. 接着, getchar(); 被用来读取输入缓冲区中的回车字符 '\n',以防止它对后续的输入产生干扰。
此时,输入缓冲区已被清空,不再包含回车字符。
1. 然后,程序提示用户确认密码,用户需要输入 'Y' 或 'N' 并按下回车键。
2. int ret = getchar(); 用来获取用户的确认选择字符。如果用户输入 'Y',则 "Yes" 被输出,否则 "No" 被输出。
这样,代码确保在确认密码前清空了输入缓冲区,以避免任何意外的字符干扰。根据用户的选择,它输出相应的确认结果。如果用户按回车键而没有输入 'Y' 或 'N',则程序会继续等待用户输入,因为回车字符 '\n' 会被 getchar() 读取并丢弃。
int main()
{
char ch = '\0';
while ((ch = getchar()) != EOF)
{
if (ch < '0' || ch > '9')
continue;
putchar(ch);
}
//只打印数字字符
return 0;
}
这段代码的目的是过滤掉输入中的非数字字符,只输出数字字符。如果用户输入一系列字符,程序将仅打印出其中的数字字符,并忽略其他字符。
1. char ch = '\0';:这一行声明了一个字符变量 ch 并将其初始化为空字符 '\0'。
2. while ((ch = getchar()) != EOF):这是一个 while 循环,它会不断执行,直到达到文件结束符(EOF)。在每次循环迭代中,程序会调用 getchar() 函数来获取一个字符,并将其存储在变量 ch 中。然后,它会检查 ch 是否等于文件结束符(EOF)。
3. if (ch < '0' || ch > '9'):在每次迭代中,程序会检查字符 ch 是否不是数字字符。这通过比较 ch 是否小于 '0' 或大于 '9' 来实现。
4. continue;:如果 ch 不是数字字符,程序会执行 continue; 语句,跳过当前循环迭代,继续下一次循环。这样,只有数字字符会被输出,其他字符将被忽略。
5. putchar(ch);:如果 ch 是数字字符,就会使用 putchar() 函数将该字符输出到屏幕上。
3.2 for循环
3.2.1 语法
for(表达式1; 表达式2; 表达式3)
循环语句;
表达式1
表达式1为 初始化部分,用于初始化循环变量的
表达式2
表达式2为 条件判断部分,用于判断循环时候终止
表达式3
表达式3为 调整部分,用于循环条件的调整
for循环执行流程图:
3.2.2 break和continue在for循环中
3.2.3 for循环与while循环对比
3.2.4 for语句的循环控制变量
建议:
1. 不可在for 循环体内修改循环变量,防止 for 循环失去控制
2. 建议for语句的循环控制变量的取值采用“前闭后开区间”写法
3.2.5 一些for循环的变种
//代码1
//for循环的判断部分省略意味这判断会恒成立
for (;;)
{
printf("hehe\n");
}
在这个代码中,for 循环的初始化部分是一个空语句 ;;,条件部分也是一个空语句,因此它们都是空的。这意味着初始化、条件判断、以及迭代部分都不存在。因此,这个循环将一直执行下去,永远不会停止。
//代码2
int i = 0;
int j = 0;
//这里打印多少个hehe?
for (i = 0; i < 3; i++)
{
for (j = 0; j < 3; j++)
{
printf("hehe\n");
}
}
外部的 for 循环控制变量 i 从0递增到2,它将执行3次。而内部的 for 循环控制变量 j 从0递增到2,它也将执行3次。
因此,内部的 printf("hehe\n"); 语句将在外部循环每次迭代时都执行3次。所以,总共会打印 3(外部循环次数) * 3(内部循环次数) = 9 次 "hehe"。
//代码3
int i = 0;
int j = 0;
//如果省略掉初始化部分,这里打印多少个hehe?
for (; i < 3; i++)
{
for (; j < 3; j++)
{
printf("hehe\n");
}
}
1. 首先i=0, 说明i<3, 此时j=0, j<3, 进入printf.
2. 打印完hehe后, j++ -> j=1, 再次打印第二个hehe.
3. 然后j++ -> j=2, j<3, 再打印第三个hehe
4. j++ -> j=3 , j<3不成立跳出循环. i++ -> i=1
5. 此时再进入for-j循环, 由于此处省略掉初始化语句, 此时j还是3, 那么这个循环就不会执行, j<3不成立, 跳出该循环.
6. 后面就单执行for-i循环. 所以综上, 打印三个hehe.
3.3 do...while循环
3.3.1 do语句的语法
do
循环语句;
while(表达式);
3.3.2 执行流程
3.3.3 do语句的特点
循环至少执行一次,使用的场景有限,所以不是经常使用.
int main()
{
int i = 1;
do
{
printf("%d ", i);
i++;
} while (i <= 10);
return 0;
}
3.3.4 do while循环中的break和continue
当在C语言中使用 do...while 循环时,break 和 continue 语句可以用来控制循环的执行。
break语句:break 语句用于立即终止当前的 do...while 循环,不再继续执行后续的迭代。这通常用于在满足某个条件时提前退出循环。
示例代码:
#include <stdio.h>
int main() {
int i = 1;
do {
if (i == 3) {
break; // 当i等于3时,立即退出循环
}
printf("%d\n", i);
i++;
} while (i <= 5);
return 0;
}
在这个示例中,当 i 的值等于3时,break 语句被触发,循环立即终止,不再打印后续的数字。
continue语句:continue 语句用于跳过当前迭代的剩余部分,然后继续下一次循环迭代。这通常用于跳过某些特定条件下的迭代,但不终止整个循环。
示例代码:
#include <stdio.h>
int main() {
int i = 1;
do {
if (i == 3) {
i++; // 增加i的值以避免陷入无限循环
continue; // 当i等于3时,跳过本次迭代
}
printf("%d\n", i);
i++;
} while (i <= 5);
return 0;
}