C语言:分支和循环【if、switch和goto语句,关系、条件和逻辑操作符,for、while和do while循环等】
一.关系、条件和逻辑操作符
在学本章主要内容之前,我们先来了解一下以下几个重要的操作符。
1.关系操作符
关系操作符用于比较两个值的关系,返回布尔值(True
或False
),常用于编程和数学逻辑中,不同编程语言符号可能略有差异。
常见关系操作符:
操作符 | 描述 | 示例 | 结果 |
---|---|---|---|
== | 等于 | 5 == 5 | 真 |
!= | 不等于 | 3 != 2 | 真 |
> | 大于 | 10 > 5 | 真 |
< | 小于 | 4 < 7 | 真 |
>= | 大于等于 | 5 >= 5 | 真 |
<= | 小于等于 | 2 <= 3 | 真 |
这些操作符常用于条件语句中,例如 if
语句,来控制程序的执行路径。
示例:
#include<stdio.h>
int main()
{
int a = 0;
scanf("%d", &a);//输入一个值
if (a == 4)//判断两个值是否相等
{
printf("a is %d\n", a);
}
else
{
printf("a isn't 4");
}
return 0;
}
值得注意的是,在C语言中“==
”是检查两个值是否相等,而“=
”是将右边的值赋值给左边,两者不要混淆。
2.条件操作符
条件操作符,又称三元运算符,是一种根据条件判断结果选择执行不同操作的运算符,语法结构紧凑,常用于简化简单的条件判断逻辑。
语法:条件表达式
?
表达式1:
表达式2
执行逻辑:
若条件表达式为真(True),则返回 “ 表达式1 ” 的值;
若为假(False),则返回 “ 表达式2 ” 的值。
示例:
#include<stdio.h>
int main()
{
int a, b;
scanf("%d %d", &a, &b);//输入a和b
int m = (a > b ? a : b);//判断结果,并将结果赋值给m
printf("m is %d\n", m);//输出m
return 0;
}
注意:
优先级:低于关系操作符和算术操作符,建议用括号明确优先级(操作符的优先原则)
如示例中的代码:
int m = (a > b ? a : b);//先算括号里的条件操作符,再将判断结果赋值给m
数据类型:“ 表达式1
” 和“ 表达式2
” 的类型需兼容,或可隐式转换为同一类型(即表达式1,2类型相同)
可读性:避免过度嵌套,复杂逻辑建议用 if-else
语句提升可读性
与 if-else
语句的区别
特性 | 条件操作符 | if-else 语句 |
---|---|---|
结构 | 一行表达式 | 多行语句块 |
返回值 | 可直接赋值或返回值 | 需通过变量接收结果 |
适用场景 | 简单条件判断 | 复杂逻辑或多条语句 |
3.逻辑操作符
逻辑操作符是用于对布尔值进行操作的运算符。
常见的逻辑操作符:
操作符 | 名称 | 描述 | 示例 | 结果 |
---|---|---|---|---|
&& | 逻辑与 | 两个表达式都为 true 时,结果为 true ,否则为 false | (5 > 3) && (2 < 4) | true |
|| | 逻辑或 | 至少一个表达式为 true 时,结果为 true ,否则为 false | (5>3) || (2>4) | true |
! | 逻辑非 | 对布尔值取反:true 变 false ,false 变 true | ! (5 == 3) | true |
逻辑操作符代码示意
逻辑与( &&
)
错误示范
#include<stdio.h>
int main()
{
int a;
scanf("%d", &a);//假如输入90
if (18 <= a <= 36)//由于操作符的优先级,在if语句里从左到右开始,先算18<=90,为真,等于1,接着1<=36为真,最后打印‘成年人’
{
printf("成年人\n");
}
return 0;
}
正确示范
#include<stdio.h>
int main()
{
int a;
scanf("%d", &a);
if (a >= 18 && a <= 36)
{
printf("成年人\n");
}
else
{
printf("不是成年人\n");
}
return 0;
}
逻辑或( ||
)
示例
#include<stdio.h>
int main()
{
int a = 0;
scanf("%d", &a);
if (a == 1 || a == 2 || a == 3)
{
printf("hahaha\n");//输入1或2或3都会打印“hahaha”
}
return 0;
}
逻辑非( !
)
示例
#include <stdio.h>
int main()
{
int num = 0;
int flag = 1;
//使用逻辑非操作符 !
if (!num)
{
printf("num的值是0\n");//会运行,因为!0 等价于 !false → true
}
if (!flag)
{
printf("flag的值是0\n");//不会运行,因为!1 等价于 !true → false
}
else
{
printf("flag的值非0\n");//会运行
}
//布尔值取反
int log_in = 0;//0表示未登录
if (!log_in)
{
printf("用户未登录,请先登录\n");//会运行
}
//结合关系表达式
int x = 10;
if (!(x > 5))
{
printf("x小于等于5\n");//不会运行,因为!(x > 5) 等价于 !(true) → false
}
else
{
printf("x大于5\n");//会运行
}
return 0;
}
在使用时,需要注意逻辑操作符的 “优先级” 以及 “结合性” ,必要时可以使用括号来明确运算顺序。
二.if、switch和goto语句
1.if 语句
if 语句的执行流程图
(1)简单 if 语句
if(condition)
{
//当condition为真时执行
}
(2)if-else 语句
if(condition)
{
//当condition为真时执行
}
else
{
//当condition为假时执行
}
(3)嵌套 if-else 语句
if(condition1)
{
//当condition1为真时执行
}
else if(condition2)
{
//当condition1为假时执行,condition2为真时执行
}
......
else
{
//当以上的conditions为假时执行
}
我们可以结合上述操作符与if语句来判断闰年
#include <stdio.h>
int main()
{
int year = 0;
scanf("%d",&year);
if (year % 4 == 0 && year % 100 != 0)
{
printf("%d 是闰年\n", year);
}
else if (year % 400 == 0)
{
printf("%d 是闰年\n", year);
}
else
{
printf("%d 不是闰年\n", year);
}
return 0;
}
也能简写下图代码,效果一样
#include <stdio.h>
int main()
{
int year = 0;
scanf("%d",&year);
if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
{
printf("%d 是闰年\n", year);
}
else
{
printf("%d 不是闰年\n", year);
}
return 0;
}
注意:
(1)条件表达式必须是整数类型(如 int
、char
)
(2)if
和else
后面默认只跟一条语句
(3)逻辑操作符(&&
、||
、!
)的优先级低于关系操作符
如代码中的:
if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
(4)C语言中 else
总是与最近的未匹配的 if
配对(避免else
悬空)
if (x > 5)
if (y > 10)
printf("x 和 y 都大\n");
else//这个 else 与内层 if 配对
printf("x 小于等于 5\n");
2.switch语句
(1)switch 基本语法
switch (expression) {
case constant1:
// 当 expression 等于 constant1 时执行的代码
break;
case constant2:
// 当 expression 等于 constant2 时执行的代码
break;
...
default:
// 当 expression 不等于任何 case 时执行的代码
}
switch语句关键:
(1)expression(条件)
:必须是整数类型(如 int
、char
)。
(2)constant(常量)
:必须是整数常量或常量表达式,且不能重复。
(3)break
:可选,用于终止 switch
语句的执行。
(4)default
:可选,当所有 case
都不匹配时执行。
执行流程
(1)计算表达式:首先计算 expression
的值。
(2)接着匹配 case
:
若 expression
等于某个 case
的常量,则执行该 case
后的代码。
若没有匹配的 case
,且存在 default
,则执行 default
后的代码。
(2)switch语句的穿透特性
穿透特性:若没有 break
,程序会继续执行下一个 case
的代码(称为 “穿透” )。
多个case合并(穿透特性)
#include <stdio.h>
int main() {
char grade = 'B';
switch (grade) {
case 'A':
case 'B': //'B'、'C'与 'A' 共用代码
case 'C':
printf("成绩合格\n");
break;
case 'D':
case 'F':
printf("成绩不合格\n");
break;
default:
printf("无效等级\n");
}
return 0;
}
没有 break
的穿透示例
#include <stdio.h>
int main() {
int num = 2;
switch (num) {
case 1:
printf("执行 case 1\n");
//无break,继续执行下一个case
case 2:
printf("执行 case 2\n");
//无break,继续执行下一个case
case 3:
printf("执行 case 3\n");
break; //有break,最终跳出switch
default:
printf("执行 default\n");
}
// 输出结果:
// 执行 case 2
// 执行 case 3
return 0;
}
注意:
(1)expression 的类型限制:必须是整数类型(如 int
、char
),不能是浮点数或字符串。
(2)case 常量的唯一性:每个 case
常量必须唯一,否则会导致编译错误。
(3)break 的使用
需要终止时:大多数情况下需要使用 break
终止执行。
需要穿透时:故意不使用 break
,让代码继续执行后续 case
。
(4)default 的位置:default
可以放在任意位置,但通常放在末尾以提高可读性。
(3)if 与 switch 的对比
特性 | switch 语句 | if-else 语句 |
---|---|---|
适用条件 | 整数常量表达式(如 int 、char ) | 任何表达式 |
语法复杂度 | 简单条件更简洁 | 复杂条件更灵活 |
执行效率 | 通过跳转表直接定位分支,效率高 | 依次判断每个条件,效率较低 |
适用场景 | 等值判断 | 范围判断、复杂逻辑 |
3.goto语句
goto
语句是一种编程语言中的控制流语句,其作用是无条件跳转到程序中指定的标签位置执行后续代码。它通过 “标签”(Label)标记目标位置,标签通常是一个标识符,后跟冒号( :
),位于目标语句之前。
(1)goto 基本语法
goto label;
...
label: // 标签声明
语句;
关键点:
label:自定义的标识符,用于标记代码位置,遵循变量命名规则(如error_handler、loop_start)。
goto label;:将程序执行跳转到指定的label处继续执行。
标签声明:label必须放在某个语句之前,用于标记跳转目标。
示例
#include <stdio.h>
int main()
{
int i, j;
for (i = 1; i <= 3; i++)
{
for (j = 1; j <= 3; j++)
{
if (i == 2 && j == 2)
{
goto exit;//当i=2且j=2时,使用goto跳出所有循环
}
}
}
exit: //标签位置
printf("hahahaha\n");
return 0;
}
(2)goto 总结
goto
语句是早期编程语言的重要控制工具,但其弊端在大型项目中尤为明显。现代软件开发更强调代码的可读性和可维护性,因此goto
通常被视为 “过时” 的特性,仅在极少数场景下谨慎使用。理解其原理有助于阅读历史代码,但在新项目中应尽量避免依赖。
三.for、while和do while循环
1.for循环
for 循环的执行流程图
C 语言中的 for
循环是一种非常常用的迭代控制结构,用于重复执行特定代码块。
(1)基本语法
for (初始化表达式; 条件表达式; 更新表达式)
{
//循环体(重复执行的代码)
}
(2)嵌套 for 循环
尝试打印打印九九乘法表
例如
#include <stdio.h>
int main()
{
int i, j;
for (i = 1; i <= 9; i++) //外层循环控制行数(被乘数
{
for (j = 1; j <= i; j++)//内层循环控制列数(乘数
{
printf("%d×%d=%-2d ", j, i, i*j);//格式化输出:左对齐,宽度为7字符
}
printf("\n");//每行结束后换行
}
return 0;
}
运行结果
(3)循环控制语句
break:提前终止整个循环
for (int i = 1; i <= 10; i++)
{
if (i == 5)
break;//当i=5时跳出循环
printf("%d ", i);//输出:1 2 3 4
}
continue:跳过当前迭代
for (int i = 1; i <= 5; i++)
{
if (i == 3)
continue;//跳过i=3的情况
printf("%d ", i);//输出:1 2 4 5
}
break 和 continue 详细解析也会在第四点提及到
2.while循环
whlie 语句的执行流程图
(1)基本语法
while (条件表达式)
{
//循环体(重复执行的代码)
}
(2)嵌套 while 循环
可以用于处理多维数据等等(如矩阵):
例如:
#include <stdio.h>
int main()
{
int i = 1;
while (i <= 3)//外层循环控制行数
{
int j = 1;
while (j <= 3)//内层循环控制列数
{
printf("%d×%d=%d\t", i, j, i*j);
j++;
}
printf("\n");
i++;
}
return 0;
}
运行结果:
(3)循环控制语句
break:提前终止整个循环
例如:
int i = 1;
while (i <= 10)
{
if (i == 5)
break;//当i=5时跳出循环
printf("%d ", i);//输出:1 2 3 4
i++;
}
continue:跳过当前迭代
例如
int i = 0;
while (i < 5)
{
i++;//注意:更新语句需放在continue前
if (i == 3)
continue;//跳过i=3的情况
printf("%d ", i);//输出:1 2 4 5
}
(4)while 循环的特点
先判断后执行:与 do-while
不同,while
可能一次都不执行。
例如:
int i = 10;
while (i < 5)
{
printf("循环体");//不会执行
}
灵活的条件:条件表达式可以是任意类型(会被隐式转换为布尔值)。
例如:
int flag = 1;
while (flag)
{ //等价于 while(flag != 0)
//循环体
flag = 0;//终止条件
}
无限循环:条件恒为真时,需通过break终止。
例如:
while (1)
{ //无限循环
printf("hahaha");
if (condition)
break;//手动终止
}
3.do while循环
do while 循环的执行流程图
C 语言中的do-while循环是一种后测试循环结构,与while和for循环不同的是,它会先执行一次循环体,再判断条件是否继续执行。
(1)基本语法
do {
//循环体(至少执行一次)
} while (条件表达式);//注意分号不能省略
例如我们来打印1到5的数字
#include <stdio.h>
int main()
{
int i = 1;
do {
printf("%d ", i);
i++;
} while (i <= 5);//最后的“ ;”别漏了
return 0;
}
运行结果:
(2)嵌套 do while 循环
用于处理多维数据:
例如:
#include <stdio.h>
int main()
{
int i = 1;
do {//外部循环
int j = 1;
do {//内部循环
printf("%d×%d=%d\t", i, j, i*j);
j++;
} while (j <= 3);
printf("\n");
i++;
} while (i <= 3);
return 0;
}
运行结果:
(3)循环控制语句
break:提前终止整个循环
例如:
int i = 1;
do {
if (i == 5)
break;//当i=5时跳出循环
printf("%d ", i);//输出:1 2 3 4
i++;
} while (i <= 10);
continue:跳过当前迭代
例如:
int i = 0;
do {
i++;
if (i == 3)
continue;//跳过i=3的情况
printf("%d ", i);//输出:1 2 4 5
} while (i < 5);
(4)do while 循环的特点
至少执行一次:即使条件一开始就不满足,循环体也会执行一次。
条件后置:适合需要先执行操作再判断条件的场景(如交互式输入)。
无限循环:条件恒为真时,需通过break终止。
值得注意的是
分号不可省略:在while (条件) ;
末尾的分号是语法必需的。
避免死循环:确保条件最终会变为假,或在循环体中使用break。
变量作用域:循环变量需在 do-while
之前定义。
错误示范:
do {
int i = 0;//每次循环重新初始化,无意义
i++;
} while (i < 5);//错误:i在此处不可见
正确示范:
int i = 0;
do {
i++;
} while (i < 5);
4.for、while和do while循环之间的对比
特性 | for 循环 | while 循环 | do-while 循环 |
---|---|---|---|
基本语法 | for (初始化; 条件; 更新) { … } | while (条件) { … } | do { … } while (条件); |
执行顺序 | 先判断条件,再执行循环体 | 先判断条件,再执行循环体 | 先执行一次循环体,再判断条件 |
最少执行次数 | 0 次(条件不满足时跳过) | 0 次(条件不满足时跳过) | 1 次(无论条件如何都先执行) |
循环变量初始化位置 | 在for 语句内部或外部 | 必须在while 之前初始化 | 必须在do-while 之前初始化 |
循环变量更新位置 | 在for 语句的更新表达式中 | 在循环体内部手动更新 | 在循环体内部手动更新 |
适用场景 | 已知循环次数(如遍历数组) | 未知循环次数,依赖条件判断 | 需要至少执行一次(如输入验证) |
四.break和continue语句解析以及用法
1.基本概念
break和continue 是编程语言中用于控制循环流程的关键字,
它们的核心区别在于:
break:立即终止当前所在的整个循环( for
/ while
/ do-while
),跳出循环体执行后续代码。
continue:跳过当前循环体中剩余的代码,直接进入下一次循环迭代。
2.语法与执行逻辑
(1)对于break语句
while (条件1)
{
//代码块1
if (条件2)
{
break; //满足条件时终止循环
}
//代码块2(break后不会执行)
}
//循环结束后执行此处
使用 break 语句效果
提前终止循环:当满足某个条件时无需继续执行循环。
例如:
//查找数组中第一个偶数
int arr[] = {1, 3, 5, 8, 7};
int i, found = 0;
for (i = 0; i < 5; i++) {
if (arr[i] % 2 == 0) {
found = 1;
break;//找到后立即跳出循环
}
}
(2)对于continue语句
while (条件)
{
//代码块1
if (条件2)
{
continue; //满足条件时跳过后续代码,进入下一次循环
}
//代码块2(continue可能跳过)
}
//循环结束后执行此处
使用 continue 语句效果
过滤无效数据:忽略某些不符合条件的迭代。
例如:
//计算数组中所有正数的和
int arr[] = {1, -2, 3, -4, 5};
int sum = 0;
for (int i = 0; i < 5; i++)
{
if (arr[i] <= 0)
{
continue;//跳过非正数
}
sum += arr[i];//仅累加正数
}
简化复杂条件判断:将不满足条件的情况提前处理。
例如:
//打印1-10之间的奇数
for (int i = 1; i <= 10; i++)
{
if (i % 2 == 0)
{
continue;//偶数直接跳过
}
printf("%d ", i);//只打印奇数
}
3.break与continue对比
特性 | break | continue |
---|---|---|
终止循环 | 完全终止当前循环 | 仅跳过当前迭代 |
执行后续代码 | 立即执行循环后的代码 | 继续下一次循环迭代 |
常见场景 | 查找结果后提前退出 | 过滤无效数据 |
多层循环影响 | 仅作用于当前层 | 仅作用于当前层 |
4.作用范围限制
(1)break
和 continue
仅作用于最近的一层循环,无法直接跳出多层循环,除非结合标签(如 goto
语句)或异常处理。
(2)在嵌套循环中使用时,需明确目标循环层级。
五.循环的嵌套
1.基本概念
C 语言中,循环嵌套是指在一个循环体内包含另一个完整的循环结构。这种技术常用于处理多维数据(如矩阵、表格)或需要多次迭代的复杂任务。
在for、while和do while循环已经涉及过各个循环控制语句,而在这里我们将进行深入了解循环语句的相互嵌套。
2.嵌套循环的基本形式
C 语言支持三种循环( for
、while
、do-while
)的任意组合嵌套。
常见形式有:
// 1. for嵌套for
for (初始化1; 条件1; 更新1)
{
for (初始化2; 条件2; 更新2)
{
//内层循环体
}
}
// 2. while嵌套while
while (条件1)
{
while (条件2)
{
//内层循环体
}
}
// 3. for嵌套while
for (初始化; 条件1; 更新)
{
while (条件2)
{
//内层循环体
}
}
// 4. 多层嵌套(如三层)
for (...) {
for (...) {
for (...) {
//最内层循环体
}
}
}
嵌套循环的执行流程
外层循环每执行一次,内层循环会完整执行所有迭代。
总执行次数 = 外层循环次数 × 内层循环次数。
例如
for (int i = 1; i <= 2; i++)//外层循环2次
{
for (int j = 1; j <= 3; j++)//内层循环3次
{
printf("i=%d, j=%d\n", i, j);
}
}
我们直接来一个典型的例子:
for 嵌套 while(打印矩形)
示例:
#include <stdio.h>
int main()
{
int rows = 3, cols = 4;
for (int i = 1; i <= rows; i++)//控制行数
{
int j = 1;
while (j <= cols)//控制列数
{
printf("* ");
j++;
}
printf("\n");
}
return 0;
}
运行结果:
3.嵌套循环中的控制语句
在这里 break
和 continue
中起的作用与 “ for
、while
和 do while
” 循环 有所不同,可以亲自去编译器里试试。
break:仅跳出当前层循环
例如:
for (int i = 1; i <= 3; i++)
{
for (int j = 1; j <= 3; j++)
{
if (j == 2)
break;//仅跳出内层循环
printf("i=%d, j=%d\n", i, j);
}
}
continue:跳过当前层的剩余代码
例如:
for (int i = 1; i <= 2; i++)
{
for (int j = 1; j <= 3; j++)
{
if (j == 2)
continue;//跳过j=2的情况
printf("i=%d, j=%d\n", i, j);
}
}
4.注意事项
(1)性能问题:多层嵌套会显著增加时间复杂度,需谨慎使用。
(2)代码可读性:嵌套层数建议不超过 3 层,否则需重构逻辑(如封装为函数)。
(3)变量作用域:内层循环可访问外层变量,但避免同名变量导致混淆。
(4)初始化位置:内层循环的变量需在内层循环内或之前初始化。
最后作者想说的是,不经一番寒彻骨,怎得梅花扑鼻香