4 C 语言控制流与循环结构的深入解读
目录
1 复杂表达式的计算过程
2 if-else语句
2.1 基本结构及示例
2.2 if-else if 多分支
2.3 嵌套 if-else
2.4 悬空的 else
2.5 注意事项
2.5.1 if 后面不要加分号
2.5.2 省略 else
2.5.3 省略 {}
2.5.4 注意点
3 while 循环
3.1 一般形式
3.2 流程特点
3.3 注意事项
3.3.1 while 后面不能加分号
3.3.2 使用花括号括起循环体
4 do-while 循环
4.1 一般形式
4.2 注意事项
5 for 循环
5.1 一般形式
5.2 执行过程
5.3 省略写法
5.3.1 省略初始化表达式
5.3.2 省略循环条件表达式
5.3.3 省略更新表达式
5.3.4 省略所有表达式
5.4 注意事项
5.4.1 for后面不能加分号
5.4.2 循环条件的正确性
6 三种循环的选择
6.1 for循环
6.2 while循环
6.3 do-while循环
7 continue 语句
7.1 语法及示例
7.2 注意事项
7.2.1 对于while循环
7.2.2 对于do-while循环
8 break 语句
8.1 语法及示例
8.2 注意事项
8.2.1 只会跳出当前所在的最内层结构
8.2.2 适用范围
9 小节判断题
10 OJ 练习
10.1 课时4作业1
10.2 课时4作业2
10.3 课时4作业3
1 复杂表达式的计算过程
程序员控制程序执行逻辑运算需要选择和循环结构,首先我们来讲选择语句,在介绍选择语句前,我们首先练习一下关系表达式与逻辑表达式。在第 3 章内容中,我们了解到算术运算符的优先级高于关系运算符、关系运算符的优先级高于逻辑与和逻辑或运算符、相同优先级的运算符从左至右进行结合等 ,简单记就是: ! > 算术运算符 > 关系运算符 > && > || > 赋值运算符,那么表达式 5>3&&8<4-!0 的最终值是多少?计算过程是怎样的?
大部分人容易想成以下错误的计算过程:
- 首先,分析表达式 5 > 3 && 8 < 4 - !0 。
- 先计算 !0 ,! 是逻辑非运算符,0 表示假,!0 就表示真,即值为 1 。
- 然后计算 4 -!0 ,即 4 - 1 ,结果为 3 。
- 接着计算 5 > 3 ,这是成立的,值为真,即 1 。
- 再计算 8 < 3 ,这是不成立的,值为假,即 0 。
- 最后计算 1 && 0 ,&& 是逻辑与运算符,只有当两个操作数都为真时结果才为真,所以整个表达式的值为假,即 0 。
然而,正确的计算过程是:先计算 5 > 3 ,因为 && 的短路性质,具体步骤如下图所示:
2 if-else语句
在你打开衣柜拿出最上面的一件衣服时,你会判断这件衣服是不是你想穿的。如果是,那么你就会穿上;如果不是,那么你就会去找其他衣服。在计算机中,我们用“如果”判断语句来实现这样的效果:“如果”判断条件(表达式)为真,就执行某个语句,反之不执行这个语句。当然,也可以“如果”判断条件(表达式)为真,就执行某个语句,反之用“否则”分支执行另一个语句,具体流程如下图 1 和图 2 所示。
在 C 语言中,if-else 语句是一种基本的控制流语句,用于根据条件表达式的真假来执行不同的代码块。这种结构允许程序进行条件判断,并根据判断结果执行相应的操作。
2.1 基本结构及示例
if-else 语句的基本结构如下:
if (condition) { // 如果condition为真(true),则执行这里的代码
} else { // 如果condition为假(false),则执行这里的代码
}
- condition:是一个表达式,其结果必须是整型(在C语言中,非零值被视为真,零被视为假)。
- if 后的花括号 {} 内包含了当 condition 为真时要执行的语句。
- else 后的花括号 {} 内包含了当 condition 为假时要执行的语句。
示例1:
假设我们要编写一个程序,根据用户输入的年龄来判断其是否成年:
#include <stdio.h>int main() {int age;printf("请输入你的年龄: ");fflush(stdout); //解决Clion中printf和scanf顺序问题scanf("%d", &age);if (age >= 18) {printf("你已成年。\n");} else {printf("你还未成年。\n");}return 0;
}
在上面这个例子中,程序首先提示用户输入年龄,然后通过一个 if-else 语句来判断年龄是否大于等于18岁。根据判断结果,程序会输出相应的信息。
输出结果如下图所示:
示例2:
判断输入值是否大于 0 ,当输入值大于 0 时,打印 “i 是大于 0”,当输入值小于等于 0 时,打印 “i 不是大于 0”,具体代码如下例所示:
#include <stdio.h>int main() {int i;// 这里使用while循环主要是为了多次运行程序,一次一次启动浪费时间while(scanf("%d",&i))if(i>0){//不要在括号后加分号printf("i is bigger than 0\n");}else{printf("i is not bigger than 0\n");}}return 0;
}
2.2 if-else if 多分支
“if 语句”和“else 语句”也可以多个同时使用(多分支语句),如下图 3 所示。
else if 是 else 语句后紧跟着另一个 if 语句的简写,它允许你在一个 if-else 结构中测试多个表达式。如果 if 语句的条件为假,则程序会检查 else if 的条件;如果 else if 的条件也为假,则继续检查下一个 else if(如果有的话);如果所有的 if 和 else if 条件都为假,并且存在一个 else 分支,则执行 else 分支中的代码。
如下面这个例子,根据用户输入的分数,程序会输出不同的评级:
#include <stdio.h> int main() { int score; printf("请输入分数(0-100): "); scanf("%d", &score); if (score >= 90) { printf("优秀\n"); } else if (score >= 80) { printf("良好\n"); } else if (score >= 60) { printf("及格\n"); } else { printf("不及格\n"); } return 0;
}
在使用 if-else if 结构创建多分支时,无论有多少个 if 语句或 else if 语句,程序都只会执行这些条件语句中的一个。这是因为程序会按照顺序检查每个条件,一旦找到第一个为真的条件,就会执行与之对应的代码块,并跳过剩余的所有 else if 和 else 分支。
如果所有的 if 和 else if 条件都不满足(即为假),并且存在 else 分支,则程序会执行 else 分支中的代码。如果连 else 分支也没有,那么程序将不会执行任何 if-else if-else 结构中的代码,而是继续执行该结构之后的代码。
下面是一个简单的示例,说明了这个过程:
#include <stdio.h> int main() { int number = 5; if (number == 1) { printf("Number is 1\n"); } else if (number == 2) { printf("Number is 2\n"); } else if (number == 5) { printf("Number is 5\n"); // 这个条件为真,因此会执行这个分支 } else if (number == 10) { printf("Number is 10\n"); // 这个条件为假,因此不会执行 } else { printf("Number is not 1, 2, 5, or 10\n"); // 也不会执行到这个分支 } return 0;
}
2.3 嵌套 if-else
else if 是处理多条件判断的首选方式,但在某些情况下,可能需要嵌套 if 语句,即在一个 if 或 else 代码块内部使用另一个 if 语句。
在下面这个例子中,我们首先检查年龄,然后根据年龄的结果,我们进一步检查身高。
#include <stdio.h> int main() { int age, height; printf("请输入年龄: "); scanf("%d", &age); printf("请输入身高(厘米): "); scanf("%d", &height); if (age >= 18) { if (height >= 170) { printf("您已经成年且身高达到或超过170厘米。\n"); } else { printf("您已经成年但身高不足170厘米。\n"); } } else { printf("您还未成年。\n"); } return 0;
}
2.4 悬空的 else
在 C 语言中,当使用嵌套的 if 语句时,必须明确每个 else 子句与哪个 if 语句相关联,以避免所谓的“悬空的 else ”问题。这个问题通常发生在有多个 if 语句连续出现,并且至少有一个 if 语句后面跟着一个 else 语句时。
编译器会根据大括号 {} 的嵌套来解析 else 子句与哪个 if 语句相关联,但如果没有明确使用大括号来限定每个 if 语句的作用域,那么 else 子句就会与最近的、尚未与 else 子句关联的 if 语句相匹配。
示例:没有使用大括号的悬空else问题
#include <stdio.h> int main() { int a = 1, b = 2; if (a > b) if (a == 1) printf("a is 1\n"); else // 这个else与哪个if相关联? printf("a is not greater than b\n"); return 0;
}
在这个例子中,else 子句实际上与内层的 if (a == 1) 相关联,而不是与外层的 if (a > b) 相关联。这是因为编译器将 else 子句与最近的、尚未匹配的if语句关联起来。所以这个例子不会输出打印任何语句!
修正:使用大括号明确作用域
为了避免这个问题,应该使用大括号 {} 来明确每个 if 语句的作用域,从而确保 else 子句与正确的 if 语句相关联。
#include <stdio.h> int main() { int a = 1, b = 2; if (a > b) { if (a == 1) { printf("a is 1\n"); } } else { printf("a is not greater than b\n"); } return 0;
}
在这个修正后的例子中,外层的 if-else 结构被大括号明确限定,因此 else 子句现在与 if (a > b) 相关联。
2.5 注意事项
2.5.1 if 后面不要加分号
在 C 语言中,“如果”(即 if 语句)后面直接加分号(;)通常是不合逻辑的,并且会导致编译错误或者逻辑上的错误,具体取决于你的意图和代码上下文。
当你看到 if 后面紧跟一个分号时,这通常意味着 if 语句的条件部分之后没有跟随任何要执行的语句,或者开发者可能误将某个语句放在了if语句的外部。这种用法往往是无效的或者是一个错误。
错误用法示例:
if (x > 0); // 这里有一个分号,但没有跟随任何语句
printf("x is positive\n"); // 这行代码将无条件执行,因为它不在if语句块内
在上面的例子中,printf 语句不会根据 x > 0 的条件来决定是否执行。因为 if 语句后有一个分号,它实际上是一个空的 if 语句,而 printf 语句总是在 if 语句之后无条件执行。
正确用法示例:
if (x > 0) { printf("x is positive\n"); // 只有当x > 0时,这行代码才会执行
}
在这个例子中,if 语句正确地包含了要执行的代码块(由大括号 {} 包围),并且该代码块内的语句仅当x > 0时才会执行。
2.5.2 省略 else
if-else 结构中的 else 部分是可以省略的。当你只关心某个条件为真时的情况,而不关心条件为假时的情况,或者条件为假时不需要执行任何操作时,就可以省略 else 部分。
省略 else 的 if 语句的基本形式如下:
if (condition) { // 当condition为真时执行的代码
}
// else部分被省略了
这里是一个简单的示例,它只在变量 x 大于 0 时打印一条消息:
#include <stdio.h> int main() { int x = 5; if (x > 0) { printf("x is positive\n"); } // 没有else部分,所以x不大于0时不会执行任何操作 return 0;
}
2.5.3 省略 {}
在 C 语言中,当 if 或 else 语句块内只有一条语句时,可以省略大括号 {}。这是为了简化代码和提高可读性(尽管在某些情况下,省略大括号可能会降低可读性,特别是当代码变得复杂或需要修改时)。
if (x > 0) printf("x is positive\n");
else printf("x is not positive\n");
在这个例子中,if 和 else 语句块内都只有一条语句,因此大括号被省略了。编译器能够正确地理解每个条件对应的执行语句。
即使只有一条语句,也建议使用大括号 {} 来明确语句块的范围,这有助于避免未来的错误。
2.5.4 注意点
选择分支(if...else):
确保条件表达式的逻辑清晰准确,避免出现歧义或错误的判断。
- 例如,不要写成类似 if (a = 5) 这样的错误形式,应该是 if (a == 5) 。
注意代码的缩进和格式,以使结构清晰易读。
多分支(if...else if...else):
按照条件的可能性和优先级合理安排分支的顺序,将最可能满足的条件放在前面。
- 例如,如果一个数值在大多数情况下处于某个特定范围内,就应该先检查这个范围。
确保每个分支的条件是互斥的,避免出现重复判断或遗漏的情况。
例如,下面是一个错误的多分支示例:
int num = 10;
if (num > 5) {printf("大于 5\n");
} else if (num > 8) { // 错误,因为如果 num 大于 8 必然大于 5,这与前面的分支逻辑冲突printf("大于 8\n");
} else {printf("小于等于 5\n");
}
而正确的应该是:
int num = 10;
if (num > 8) {printf("大于 8\n");
} else if (num > 5) {printf("大于 5 小于等于 8\n");
} else {printf("小于等于 5\n");
}
嵌套 if...else:
避免过度嵌套,以免代码变得复杂难以理解。如果嵌套层次过多,可能需要考虑重构代码。
同样要注意条件的准确性和逻辑的清晰性。
- 例如,在内层的 if...else 中,要明确其与外层条件的关系。
对于复杂的嵌套结构,可以添加适当的注释来解释代码的意图。
3 while 循环
while 循环是 C 语言中的一种基本控制流语句,它实现了 “当型” 循环结构。
这种循环结构的特点是:在执行循环体内的语句之前,首先会评估一个条件表达式的值。如果条件表达式的值为非0(即真),则执行循环体内的语句;执行完毕后,再次评估条件表达式的值,如果仍为真,则重复执行循环体内的语句,如此往复,直到条件表达式的值为0(即假)时,循环结束,程序继续执行while循环之后的代码。
3.1 一般形式
while (表达式) { // 循环体(包含零条或多条语句)
}
- 表达式:这是每次循环开始前被评估的条件表达式。如果其值为真(非零),则执行循环体。
- 循环体:包含了一组将被重复执行的语句。这些语句在条件为真时执行。
3.2 流程特点
先判断后执行:while循环首先检查条件表达式的值。如果条件为真,则执行循环体;如果为假,则跳过循环体,继续执行while循环之后的语句。
条件更新:为了避免死循环(即程序无限期地重复执行循环体),通常需要在循环体内部包含一些更新条件表达式的操作,以确保在某个时刻条件表达式的值会变为假,从而结束循环。
- 程序首先检查 while 循环的条件表达式。
- 如果条件为真(即,条件表达式的结果是非零值),则执行循环体内的语句。
- 完成循环体内的所有语句后,程序再次回到步骤1,重新检查条件表达式。
- 如果条件仍然为真,则再次执行循环体。这个过程会一直重复,直到条件变为假为止。
- 一旦条件变为假,程序将跳过循环体的执行,并继续执行 while 循环之后的下一条语句。
示例:
在下面这个例子中,while 循环会重复执行,直到变量 i 的值达到5(不再小于5),此时条件表达式 i < 5 变为假,循环结束。循环体内的 printf 语句用于打印变量i的当前值,而 i++ 语句则负责更新变量 i 的值,以确保循环能够正常结束。
#include <stdio.h> int main() { int i = 0; while (i < 5) { // 条件表达式 printf("%d\n", i); // 循环体 i++; // 更新条件表达式中的变量,确保循环能够在某个点结束 等价于 i += 1 } return 0;
}
输出结果如下所示:
下面来看使用 while 语句计算1到100之间所有整数之和的累加例子,如下例所示:
#include <stdio.h>int main() {int i=1,total=0;while( i <= 100 )//不能在 while 后面加分号{total += i;i++;//循环内要有使循环趋近于假的操作}printf("%d\n",total); // 5050return 0;
}
3.3 注意事项
3.3.1 while 后面不能加分号
在C语言中,如果在 while 语句的条件表达式后面直接加上一个分号(;),那么这个分号实际上被视为 while 循环的“空语句”体。这意味着 while 循环将无限地评估条件表达式,但由于循环体为空(即不执行任何操作),所以程序看起来像是没有做任何事情,但实际上它正忙于不断地检查条件表达式的值,从而导致了所谓的 “空循环” 或 “无限(死)循环”。
错误的示例:
int i = 0;
while (i < 5); { // 注意这里的分号 printf("%d\n", i);
}
// 这段代码会导致无限循环,因为 while 循环的“体”实际上是空的,
// 而下面的花括号块则与 while 循环无关,会独立地执行一次(如果它不是位于函数之外的话),
// 但这通常不是我们想要的结果。
正确的做法:
int i = 0;
while (i < 5) { printf("%d\n", i); i++; //循环内要有使循环趋近于假的操作
}
// 这段代码正确地使用了while循环,循环体会在条件为真时执行。
3.3.2 使用花括号括起循环体
即使while循环体内只有一条语句,也建议使用花括号 {} 将其括起来。这有几个好处:
- 清晰性:花括号明确地标出了循环体的开始和结束,使得代码更加易于阅读和理解。
- 可扩展性:随着程序的发展,你可能需要在循环体内添加更多的语句。如果一开始就使用了花括号,那么添加新语句时就不需要改变循环的结构。
- 避免错误:如果不使用花括号,并且后来尝试在“单条语句”的循环体内添加更多语句,而没有意识到原来的语句已经被视为循环体,那么这些新添加的语句将不会被循环控制,这可能会导致难以发现的错误。
推荐的做法:
int i = 0;
while (i < 5) { printf("%d\n", i); i++; //循环内要有使循环趋近于假的操作// 如果将来需要添加更多语句,这里可以直接添加,而不需要改变循环的结构。
}
4 do-while 循环
do-while 循环是C语言中另一种基本的循环控制结构,与 while 循环相似,但它有一个关键的区别:do-while 循环至少会执行一次循环体内的语句,即使条件表达式的值在第一次评估时就为假。这是因为 do-while 循环的控制结构是先执行循环体,然后再评估条件表达式。【先执行后判断】
4.1 一般形式
do { // 循环体(至少执行一次)
} while (表达式);
- 循环体:包含了一组在循环中重复执行的语句。在do-while 循环中,这些语句至少会被执行一次。
- 表达式:这是一个条件表达式,其值在每次循环体执行完毕后被评估。如果条件为真(非零),则循环继续;如果条件为假(零),则循环结束。
工作原理:
- 执行循环体内的语句。
- 评估条件表达式的值。
- 如果条件为真,则返回步骤1并重复执行循环体。
- 如果条件为假,则退出循环,继续执行 do-while 循环之后的语句。
示例:
#include <stdio.h> int main() { int i = 0; do { printf("%d\n", i); i++; } while (i < 5); return 0;
}
在这个例子中,do-while 循环会首先执行循环体内的语句,即打印变量 i 的当前值,并将 i 的值增加1。然后,它评估条件表达式 i < 5。由于初始时 i 的值为0,小于5,因此条件为真,循环继续执行。这个过程会一直重复,直到 i 的值达到5,条件表达式变为假,循环结束。
4.2 注意事项
与 while 循环不同,do-while 循环至少会执行一次循环体,即使条件表达式的初始值为假。
为了避免无限循环,必须在循环体内包含某种更新条件表达式的操作,以确保在某个时刻条件会变为假。
在某些情况下,当你知道循环体至少需要执行一次时,do-while 循环是更好的选择。然而,在大多数情况下,while 循环和 for 循环(特别是后者,因为它更灵活)是更常用的循环控制结构。
5 for 循环
C 语言中的 for 循环语句使用最为灵活,不仅可以用于循环次数已经确定的情况,而且可以用于循环次数不确定而只给出循环结束条件的情况,它完全可以代替 while 循环语句。
5.1 一般形式
for (初始化表达式; 循环条件表达式; 更新表达式) { // 循环体
}
初始化表达式:在循环开始前执行,通常用于初始化一个或多个循环控制变量。它只执行一次。
循环条件表达式:在每次循环开始前求值。如果表达式的结果为真(非零),则执行循环体;如果为假(零),则退出循环。这是决定是否继续循环的关键。
更新表达式:在每次循环体执行完毕后执行,通常用于更新或递增/递减循环控制变量。
循环体:当循环条件为真时,重复执行的语句块。
5.2 执行过程
(1)初始化表达式执行
- 在循环开始前,首先执行初始化表达式。这通常用于设置循环控制变量的初始值。此表达式只执行一次。
(2)求解循环条件表达式
- 接下来,求解循环条件表达式的值。如果表达式的值为真(即非零),则执行接下来的步骤;如果表达式的值为假(即零),则直接跳过循环体,执行 for 循环之后的语句。
(3)执行循环体
- 如果循环条件表达式的值为真,则执行循环体内的语句。循环体可以包含一条或多条语句,用于完成循环需要重复执行的任务。
(4)求解更新表达式
- 执行完循环体后,求解更新表达式。这通常用于更新循环控制变量的值,为下一次循环迭代做准备。更新表达式执行完毕后,控制流回到第(2)步。
(5)转回第(2)步继续执行
- 控制流回到第(2)步,再次求解循环条件表达式的值。如果条件仍为真,则重复执行第(3)步和第(4)步;如果条件为假,则跳出循环。
(6)循环结束,执行for语句下面的语句
- 当循环条件表达式的值变为假时,循环结束。此时,程序将执行 for 循环之后的语句。
示例1:
#include <stdio.h> int main() { for (int i = 1; i <= 5; i++) { printf("%d\n", i); } return 0;
}
在上面这个例子中,i = 1 是初始化表达式,它设置循环控制变量 i 的初始值为1。i <= 5 是循环条件表达式,只要i的值小于或等于 5,循环就会继续。i++ 是更新表达式,它在每次循环迭代结束时将i的值增加1。循环体是 printf 语句,用于打印当前的 i 值。
示例2:
#include <stdio.h>int main() {int sum = 0;for (int i = 1; i <= 100; i++) {sum += i;}printf("1 到 100 之间的所有整数之和为: %d\n", sum);return 0;
}
我们首先初始化一个变量 sum 为 0,然后通过 for 循环从 1 迭代到 100。在每次循环中,将当前的整数 i 累加到 sum 中。最后,打印出求和的结果。
5.3 省略写法
在C语言中,for 循环的初始化表达式、循环条件表达式和更新表达式都是可选的,但两个分号(;)必须保留以维持语法结构。
5.3.1 省略初始化表达式
当省略初始化表达式时,循环变量(如果有的话)需要在循环外部进行初始化。
int i = 0; // 外部初始化
for (; i < 5; i++) { printf("%d\n", i);
}
这里,i 在for 循环外部被初始化为 0 ,循环条件检查 i 是否小于5,如果是,则执行循环体,并在每次迭代后通过更新表达式 i++ 来增加i的值。
5.3.2 省略循环条件表达式
如果省略循环条件表达式,那么循环将变成无限循环,除非在循环体内部有某种机制(如break语句)来跳出循环。
int i = 0;
for (; ; i++) { if (i >= 5) break; // 使用break语句来跳出循环 printf("%d\n", i);
}
在这个例子中,由于循环条件表达式被省略了,所以循环会一直执行,直到遇到 break 语句。
5.3.3 省略更新表达式
当省略更新表达式时,循环控制变量的值不会在每次迭代后自动更新。这通常意味着需要在循环体内部手动更新循环控制变量的值,或者依赖于循环体内部的其他逻辑来确保循环能够正确终止。
int i = 0;
for (; i < 5; ) { printf("%d\n", i); i++; // 手动更新循环控制变量
}
在这个例子中,尽管更新表达式被省略了,但我们在循环体的末尾手动增加了i的值,以确保循环能够正确终止。
5.3.4 省略所有表达式
for (;;) { // 死循环即无限循环,直到遇到break或其他跳出循环的语句
}
这种形式的 fo r循环通常用于需要持续运行直到某个条件满足(并在循环体内检查该条件)的情况。
省略 for 循环中的表达式可以提供灵活性,但也需要开发者更加小心地管理循环的控制流程,以避免无限循环或意外的程序行为。在实际编程中,建议仅在确实需要时才省略这些表达式,并确保循环的逻辑是正确的。
建议都写上,直观明了~
5.4 注意事项
5.4.1 for后面不能加分号
如果你在 for 循环的圆括号之后直接加了一个分号(;),那么你就定义了一个空的循环体。这意味着循环体将不会包含任何代码,而更新表达式(如果有的话)仍然会在每次循环时执行。
for (int i = 0; i < 10; i++) { // 正确的循环体
} // 错误的用法,循环体为空
for (int i = 0; i < 10; i++); { // 这段代码实际上不在for循环内 printf("%d\n", i); // 注意:这里i在循环结束后才可用,但在这个上下文中可能产生编译错误或未定义行为
} // 更常见的错误可能是这样的
for (int i = 0; i < 10; i++); // 空的for循环,但分号结束
printf("%d\n", i); // 这段代码在for循环之后执行,i的值是10(如果循环条件正确)
5.4.2 循环条件的正确性
确保循环条件能够准确地控制循环的开始、结束和迭代次数,避免出现无限循环或过早结束循环的情况。
// 错误示例:循环条件错误导致无限循环
for (int i = 0; i < 10; i--) {// 循环体
}
6 三种循环的选择
6.1 for循环
适用情况:当你知道循环需要执行的确切次数时,for循环是最佳选择。它也适用于需要初始化变量、检查条件和更新变量的场景。
优点:for循环的结构清晰,将初始化、条件和更新表达式集中在一起,使得循环的逻辑一目了然。
使用建议:在大多数需要计数或迭代固定次数的情况下,使用for循环。
6.2 while循环
适用情况:当你不知道循环需要执行的确切次数,但有一个持续检查的条件时,while循环是合适的。
优点:while循环提供了更大的灵活性,因为它不需要在循环开始前初始化变量或在每次迭代后更新变量。
使用建议:在需要基于条件持续执行循环,且循环次数不确定或条件与循环控制变量的更新不直接相关时,使用while循环。
6.3 do-while循环
适用情况:do-while循环与while循环类似,但它至少会执行一次循环体,无论条件是否为真。
优点:确保循环体至少被执行一次,这在某些情况下是有用的,比如当你需要至少执行一次操作来初始化或检查某些条件时。
使用建议:在需要至少执行一次循环体,然后基于条件决定是否继续执行时,使用do-while循环。
for 循环的可读性要比 while 循环的好,所以能使用 for 循环时不要强制改为 while 循环。
7 continue 语句
C 语言中的 continue 语句是一种流程控制语句,它用于跳过当前循环迭代中 continue 语句之后的剩余代码,并立即开始下一次循环迭代(如果有的话)。这意味着continue语句仅影响它所在的循环的当前迭代,而不会影响循环的继续执行或退出。
continue语句可以用在for循环、while循环和do-while循环中,但不能用在函数外部或任何其他控制结构(如if语句)中。
7.1 语法及示例
continue;
使用场景:continue 语句通常用于在循环体中遇到某些特定条件时,跳过当前迭代中的剩余部分,直接开始下一次迭代。这可以用于忽略某些不需要处理的元素或情况。
示例:对1到100之间的所有奇数进行求和
#include <stdio.h> int main() { int sum = 0; // 用于存储奇数和的变量 for (int i = 1; i <= 100; i++) { if (i % 2 == 0) { continue; // 如果i是偶数,则跳过本次循环的剩余部分 } sum += i; // 如果是奇数,则将其加到sum上 } printf("1到100之间的奇数和为: %d\n", sum); //2500return 0;
}
7.2 注意事项
continue 语句只能在循环体内使用!
在 while 和 do-while 循环中使用continue语句时,需要特别小心,以确保不会跳过更新循环条件或执行使循环趋近于结束的必要语句。如果这样做了,就可能导致无限循环或不符合预期的行为。
7.2.1 对于while循环
在while循环中,循环条件是在每次迭代之前检查的。如果 continue 语句跳过了更新循环控制变量的代码,并且循环条件没有在其他地方被更新为可能使循环结束的值,那么循环可能会无限进行下去。
示例(错误用法):
int i = 0;
while (i < 10) { if (i % 2 != 0) { continue; // 跳过奇数 } printf("%d ", i); i++; // 确保循环条件有机会变为假
}
正确的 while 循环示例:
int i = 0;
while (i < 10) { if (i % 2 != 0) { i++; //这里也要来一下,防止continue跳过了continue; // 跳过奇数 } printf("%d ", i); i++; // 确保循环条件有机会变为假
}
7.2.2 对于do-while循环
在do-while循环中,循环体至少执行一次,然后检查循环条件。如果 continue 语句被用来跳过更新循环控制变量的代码,并且循环条件在循环体内没有在其他地方被更新为可能使循环结束的值,那么循环也可能继续执行,直到外部干预(如break语句或程序的其他部分)。
do-while循环的示例:
#include <stdio.h>int main() {int i = 0;do {if (i % 2 != 0) {i++;continue; // 跳过奇数}printf("%d ", i);i++; // 确保循环条件有机会变为假} while (i < 10);return 0;
}
8 break 语句
C 语言中的 break 语句是一种流程控制语句,它用于立即退出它所在的循环(for、while、do-while)或 switch 语句。当执行到 break 语句时,程序会跳过当前循环体中break语句之后的所有语句(如果有的话),并且跳出循环或 switch 语句,继续执行循环或 switch 之后的语句。
8.1 语法及示例
break;
示例:程序从1开始累加,当累加的和大于2000时,结束 for 循环,并打印此时 total 的值和循环变量 i 的值。
#include <stdio.h>int main() {int i = 1; // 循环变量初始化int total = 0; // 累加和初始化for (; i <= 1000; i++) { // 注意这里去掉了i的初始化,因为我们在循环外已经初始化了itotal += i; // 累加if (total > 2000) { // 检查累加和是否大于2000break; // 如果是,则退出循环}}// 打印结果printf("当累加的和大于2000时,i的值为:%d\n", i); // 63printf("此时,累加和total的值为:%d\n", total); //2016return 0;
}
8.2 注意事项
8.2.1 只会跳出当前所在的最内层结构
在嵌套的循环或 switch 语句中使用 break 时,它只会跳出当前所在的最内层结构,而不是所有的嵌套结构。如果想要跳出多层嵌套,可能需要额外的逻辑控制。
for (int i = 0; i < 5; i++) {for (int j = 0; j < 5; j++) {if (j == 3) {break; // 只会跳出内层的 for 循环,而不是外层的}}
}
8.2.2 适用范围
break 通常用于 switch 语句和循环结构(如 for、while、do-while)中。在其他地方使用可能会导致语法错误或不符合预期的逻辑。
在switch语句中的使用(后期课程学习,这里先引入下)
在 switch 语句中,break 语句用于防止代码自动“落入”下一个 case 块。如果没有 break 语句,一旦某个 case 的条件满足,从该 case 开始到 switch 语句结束(或遇到下一个 break 语句)之间的所有 case 块都会被执行,这通常不是预期的行为。
#include <stdio.h> int main() { char grade = 'B'; switch(grade) { case 'A': printf("Excellent!\n"); break; case 'B': case 'C': printf("Well done\n"); break; case 'D': printf("You passed\n"); break; case 'F': printf("Better try again\n"); break; default: printf("Invalid grade\n"); } return 0;
}
9 小节判断题
1、关系表达式或者逻辑表达式最终的结果都是 1 或者 0 ?
A 正确 B 错误
答案:A
解释:正确的,这个需要记住。
2、if 可以单独使用,不一定非要有 else ?
A 正确 B 错误
答案:A
解释:if 判断可以单独使用,这个需要记住。
3、 else 可以单独使用,不一定非要有 if ?
A 正确 B 错误
答案:B
解释:else 必须和 if 配对使用,不可以单独存在,这是语法要求。
4、代码编写 int a=5; if(3<a<10) 这种编写方式是否正确 ?
A 正确 B 错误
答案:B
解释:在程序中是错误的。首先,无论 a 是大于 3 还是小于 3,对于 3<a 这个表达式只有 1 或 0 两种结果。由于 1 和 0 都是小于 10 的,所以无论 a 的值为多少,这个表达式的值始终为真,因此在判断变量 a 是否大于 3 且同时小于 10 时,要写成 a>3 && a<10 ,这才是正确的写法。
5、 while(i<10); 这条语句没有问题,程序可以正常运行?
A 正确 B 错误
答案:B
解释:当在 while 语句的小括号后面加分号后,会造成死循环。
6、for(:;) 语句可以这么写,把 3 个表达式都省略,只剩余两个分号?
A 正确 B 错误
答案:A
解释:for 语句中的两个分号是不可以省略的,其他的表达式都可以省略,但是我们一般不会省略表达式,因为 for 的 3 个表达式使用起来非常方便
7、continue 语句的作用为结束本次循环,即跳过循环体中下面尚未执行的语句,接着进行是否执行下一次循环的判断?
A 正确 B 错误
答案:A
解释:这是 continue 的作用,需要记住,在 while 循环中使用 continue 要小心, continue 之前要有让 while 循环判断趋近于假的操作,避免死循环。
8、 break 语句的作用是结束整个循环过程?
A 正确 B 错误
答案:A
解释: break 语句是结束整个循环,和 continue 的区别一定要清晰,理解可以再次看视频,通过我们视频里的实例清晰理解。
10 OJ 练习
10.1 课时4作业1
这道题主要是练习如何通过循环操作,将一个整型数逆置,我们判断逆置后的整型数如果和
最初的数相等,那么它就是对称数,如果不相等,就不是对称数。
#include <stdio.h>int main() {// inputNumber : 用于存储输入的数字// reversedNumber : 用于存储反转后的数字,必须初始化,后需要对其做乘10处理// tempNumber : 临时存储输入的数字,用于后续比较int inputNumber,reversedNumber = 0, tempNumber;// 从用户处获取输入的数字scanf("%d", &inputNumber);// 先把输入的数据保护好tempNumber = inputNumber;// 这个循环用于将输入的数字反转while (inputNumber) {reversedNumber = reversedNumber * 10 + inputNumber % 10; // 将当前数字的个位添加到反转数字中,并乘以 10 为下一位做准备inputNumber /= 10; // 去除当前数字的个位}// 判断输入的数字是否与反转后的数字相等if (tempNumber == reversedNumber) {printf("yes"); // 是对称数,输出"yes"} else {printf("no"); // 不是对称数,输出"no"}return 0;
}
10.2 课时4作业2
这个题目主要考察的是 for 循环的使用。
#include <stdio.h>int main() {int number,factorial = 1; // 初始化阶乘结果为1 // 读取用户输入的数scanf("%d", &number);// 使用for循环计算阶乘 for(int i = 1; i <= number; i++) {factorial *= i; // 累乘计算阶乘 }// 输出结果 printf("%d\n", factorial);return 0;
}
10.3 课时4作业3
这个题目考察的是多层循环的使用,有 4 种类型钞票,那么我们选择 4 层循环,只需在最
内层的循环判断钞票组合后的值为 100,就可以完美解决这个问题。注意控制每一层循环的
边界,不要过大,当然如果每个边界都写 40,在 OJ 也可以 AC。
10元纸币的数量不能为0(至少1张),且不能超过9张(否则总金额会超过100元) 。
5元纸币的数量也不能为0(至少1张),(100-10-2-1) / 5=17,最多17张。
2元纸币的数量也不能为0(至少1张),最多37张。
1元纸币的数量也不能为0(至少1张),最多37张。
所以,四层循环如下:
#include <stdio.h>int main() {int counter = 0; // 用于计数满足条件的换法// 外层循环:10元纸币的数量,从1到9张for (int i = 1; i <= 9; ++i) {// 注释:10元纸币的数量不能为0(至少1张),且不能超过9张(否则总金额会超过100元)// 第二层循环:5元纸币的数量,从1到17张for (int j = 1; j <= 17; ++j) {// 注释:5元纸币的数量也不能为0(至少1张),(100-10-2-1)/5=17// 第三层循环:2元纸币的数量,从1到37张for (int k = 1; k <= 37; ++k) {// 注释:2元纸币的数量也不能为0(至少1张),最多37张// 第四层循环:1元纸币的数量,从1到37张for (int l = 1; l <= 37; ++l) {// 注释:1元纸币的数量也不能为0(至少1张),最多37张// 检查当前组合是否满足总金额为100元和总张数为40张的条件if (100 == i * 10 + j * 5 + k * 2 + l * 1 && i + j + k + l == 40) {counter++; // 如果满足条件,则计数器加1}}}}}// 输出满足条件的换法总数printf("%d\n", counter);return 0;
}
相关文章:
4 C 语言控制流与循环结构的深入解读
目录 1 复杂表达式的计算过程 2 if-else语句 2.1 基本结构及示例 2.2 if-else if 多分支 2.3 嵌套 if-else 2.4 悬空的 else 2.5 注意事项 2.5.1 if 后面不要加分号 2.5.2 省略 else 2.5.3 省略 {} 2.5.4 注意点 3 while 循环 3.1 一般形式 3.2 流程特点 3.3 注…...
vue排序
onEnd 函数示例,它假设 drag.value 是一个包含多个对象(每个对象至少包含 orderNum 和 label 属性)的数组,且您希望在拖动结束后更新所有元素的 orderNum 以反映新的顺序: function onEnd(e) { // 首先,确…...
agv叉车slam定位精度测试标准化流程
相对定位精度 条件:1.5m/s最高速度;基于普通直行任务 数据采集(3个不同位置的直行任务,每个任务直行约10m,每个10次) 测量每次走过的实际距离,与每次根据定位结果算得的相对距离,两…...
实战打靶集锦-31-monitoring
文章目录 1. 主机发现2. 端口扫描3. 服务枚举4. 服务探查4.1 ssh服务4.2 smtp服务4.3 http/https服务 5. 系统提权5.1 枚举系统信息5.2 枚举passwd文件5.3 枚举定时任务5.4 linpeas提权 6. 获取flag 靶机地址:https://download.vulnhub.com/monitoring/Monitoring.o…...
小程序-模板与配置
一、WXML模板语法 1.数据绑定 2.事件绑定 什么是事件 小程序中常用的事件 事件对象的属性列表 target和currentTarget的区别 bindtap的语法格式 在事件处理函数中为data中的数据赋值 3.事件传参与数据同步 事件传参 (以下为错误示例) 以上两者的…...
交叉编译aarch64的Qt5.12.2,附带Mysql插件编译
一、配置交叉编译工具链 1、交叉编译工具链目录 /opt/zlg/m3568-sdk-v1.0.0-ga/gcc-buildroot-9.3.0-2020.03-x86_64_aarch64-rockchip-linux-gnu/bin/aarch64-rockchip-linux-gnu-g /opt/zlg/m3568-sdk-v1.0.0-ga/gcc-buildroot-9.3.0-2020.03-x86_64_aarch64-rockchip-linu…...
好用的Ubuntu下的工具合集[持续增加]
1. 终端工具 UBUNTU下有哪些好用的终端软件? - 知乎 (zhihu.com) sudo apt install terminator...
Xcode 16 beta3 真机调试找不到 Apple Watch 的尝试解决
很多小伙伴们想用 Xcode 在 Apple Watch 真机上调试运行 App 时却发现:在 Xcode 设备管理器中压根找不到对应的 Apple Watch 设备。 大家是否已将 Apple Watch 和 Mac 都重启一万多遍了,还是束手无策。 Apple Watch not showing in XCodeApple Watch wo…...
Three.JS 使用RGBELoader和CubeTextureLoader 添加环境贴图
导入RGBELoader模块: import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader.js"; 使用 addRGBEMappingk(environment, background,url) {rgbeLoader new RGBELoader();rgbeLoader.loadAsync(url).then((texture) > {//贴图模式 经纬…...
k8s logstash多管道配置
背景 采用的是标准的ELKfilebeat架构 ES版本:7.17.15 logstash版本:7.17.15 filebeat版本: 7.17.15 helm版本:7.17.3,官方地址:elastic/helm-charts 说一下为什么会想到使用多管道的原因 我们刚开始…...
【CMU博士论文】结构化推理增强大语言模型(Part 0)
问题 :语言生成和推理领域的快速发展得益于围绕大型语言模型的用户友好库的普及。这些解决方案通常依赖于Seq2Seq范式,将所有问题视为文本到文本的转换。尽管这种方法方便,但在实际部署中存在局限性:处理复杂问题时的脆弱性、缺乏…...
Odoo创建一个自定义UI视图
Odoo能够为给定的模型生成默认视图。在实践中,默认视图对于业务应用程序来说是绝对不可接受的。相反,我们至少应该以合乎逻辑的方式组织各个字段。 视图在带有Actions操作和Menus菜单的 XML 文件中定义。它们是模型的 ir.ui.view 实例。 列表视图 列表视…...
Day16_集合与迭代器
Day16-集合 Day16 集合与迭代器1.1 集合的概念 集合继承图1.2 Collection接口1、添加元素2、删除元素3、查询与获取元素不过当我们实际使用都是使用的他的子类Arraylist!!! 1.3 API演示1、演示添加2、演示删除3、演示查询与获取元素 2 Iterat…...
html2canvas + jspdf 纯前端HTML导出PDF的实现与问题
前言 这几天接到一个需求,富文本编辑器的内容不仅要展示出来,还要实现展示的内容导出pdf文件。一开始导出pdf的功能是由后端来做的,然后发现对于宽度太大的图片,导出的pdf文件里部分图片内容被遮盖了,但在前端是正常显…...
【JVM】JVM调优练习-随笔
JVM实战笔记-随笔 前言字节码如何查看字节码文件jclasslibJavapArthasArthurs监控面板Arthus查看字节码信息 内存调优内存溢出的常见场景解决内存溢出发现问题Top命令VisualVMArthas使用案例 Prometheus Grafana案例 堆内存情况对比内存泄漏的原因:代码中的内存泄漏并发请求问…...
如何解决 CentOS 7 官方 yum 仓库无法使用
一、背景介绍 编译基于 CentOS 7.6.1810 镜像的 Dockerfile 过程中,执行 yum install 指令时,遇到了错误:Could not resolve host: mirrorlist.centos.org; Unknown error。 二、原因分析 官方停止维护 CentOS 7。该系统内置的 yum.repo 所使用的域名 mirrorlist.centos.o…...
分布式唯一id的7种方案
背景 为什么需要使用分布式唯一id? 如果我们的系统是单体的,数据库是单库,那无所谓,怎么搞都行。 但是如果系统是多系统,如果id是和业务相关,由各个系统生成的情况下,那每个主机生成的主键id就…...
嵌入式物联网在医疗行业中的应用——案例分析
作者主页: 知孤云出岫 目录 嵌入式物联网在医疗行业中的应用——案例分析引言1. 智能病房监控1.1 实时患者监控系统 2. 智能医疗设备管理2.1 设备使用跟踪与维护 3. 智能药物管理3.1 药物分配与跟踪 4. 智能远程医疗4.1 远程患者监控与诊断 总结 嵌入式物联网在医疗行业中的应…...
C语言 底层逻辑详细阐述指针(一)万字讲解 #指针是什么? #指针和指针类型 #指针的解引用 #野指针 #指针的运算 #指针和数组 #二级指针 #指针数组
文章目录 前言 序1:什么是内存? 序2:地址是怎么产生的? 一、指针是什么 1、指针变量的创建及其意义: 2、指针变量的大小 二、指针的解引用 三、指针类型存在的意义 四、野指针 1、什么是野指针 2、野指针的成因 a、指…...
【人工智能大模型】文心一言介绍以及基本使用指令
目录 一、产品背景与技术基础 二、主要功能与特点 基本用法 指令的使用 注意事项 文心一言(ERNIE Bot)是百度基于其文心大模型技术推出的生成式AI产品。以下是对文心一言的详细介绍: 一、产品背景与技术基础 技术背景:百度…...
AI绘画入门实践|Midjourney 的模型版本
模型分类 Midjourney 的模型主要分为2大类: 默认模型:目前包括:V1, V2, V3, V4, V5.0, V5.1, V5.2, V6 NIJI模型:目前包括:NIJI V4, NIJI V5, NIJI V6 模型切换 你在服务器输入框中输入 /settings: 回车后…...
Web3时代的教育技术革新:智能合约在学习管理中的应用
随着区块链技术的发展和普及,Web3时代正在为教育技术带来前所未有的革新和机遇。智能合约作为区块链技术的核心应用之一,不仅在金融和供应链管理等领域展示了其巨大的潜力,也在教育领域中逐渐探索和应用。本文将探讨智能合约在学习管理中的具…...
云计算实训室的核心功能有哪些?
在当今数字化转型浪潮中,云计算技术作为推动行业变革的关键力量,其重要性不言而喻。唯众,作为教育实训解决方案的领先者,深刻洞察到市场对云计算技能人才的迫切需求,精心打造了云计算实训室。这一实训平台不仅集成了先…...
芯科科技第五届物联网开发者大会走进世界各地,巡回开启注册
中国,北京 – 2024年7月18日 – 致力于以安全、智能无线连接技术,建立更互联世界的全球领导厂商Silicon Labs(亦称“芯科科技”,NASDAQ:SLAB)今日宣布,其2024年Works With开发者大会现正开放注册…...
Python创建Excel表和读取Excel表的基础操作
下载openpyxl第三方库 winr打开命令行输入cmd 这个如果不行可以试试其他方法,在运行Python代码的软件里也有直接下载的地方,可以上网搜索 创建Excel表 示例代码:最后要记得保存,可以加一句提示语句。 import openpyxl lst[100,…...
JVM(day2)经典垃圾收集器
经典垃圾收集器 Serial收集 使用一个处理器或一条收集线程去完成垃圾收集工作,更重要的是强调在它进行垃圾收集时,必须暂停其他所有工作线程,直到它收集结束。 ParNew收集器 ParNew 收集器除了支持多线程并行收集之外,其他与 …...
华为od机试真题 — 分披萨(Python)
题目描述 “吃货”和“馋嘴”两人到披萨店点了一份铁盘(圆形)披萨,并嘱咐店员将披萨按放射状切成大小相同的偶数个小块。 但是粗心服务员将披萨切成了每块大小都完全不同奇数块,且肉眼能分辨出大小。 由于两人都想吃到最多的披萨,他们商量…...
ubuntu22.04 安装boost
下载boost压缩包,我这里上传了一份1_81_0版本tar -xzvf boost_1_81_0.tar.gzcd boost_1_81_0/sudo apt install build-essential g autotools-dev libicu-dev libbz2-dev -ysudo ./bootstrap.sh --prefix/usr/./b2sudo ./b2 install 上述7步完成后,相关…...
基于JAVA+SpringBoot+uniapp的心理小程序(小程序版本)
✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 技术范围:SpringBoot、Vue、SSM、HLMT、Jsp、SpringCloud、Layui、Echarts图表、Nodejs、爬…...
C语言 ——— 输入两个正整数,求出最小公倍数
目录 何为最小公倍数 题目要求 代码实现 方法一:暴力求解法(不推荐) 方法二:递乘试摸法(推荐) 何为最小公倍数 最小公倍数是指两个或者多个正整数(除了0以外)的最小的公共倍数…...
wordpress 主题 保存/人工智能培训班收费标准
树的遍历 Description 给定某二叉树的前序序列和中序序列,输出该二叉树的后序序列。(输入的前序遍历和中序遍历的结果中都不含重复的数字) 第一行是n 接下来一行有n个数表示前序序列 最后一行是中序序列 对于30%的数据,n<20…...
网站建设与推广推荐/看seo
线下发家的时髦品牌,线上线下互通早已经成为他们的根本装备。但近年来,发迹于线上的时髦品牌纷繁涉足线下实体商场。近日,汇集了近30个淘宝知名服饰品牌的“淘宝声调Taostyle”在杭州嘉里中心开业。一批在淘宝上成长起来的年轻品牌࿰…...
新乡网站建设公司黄页/discuz论坛seo设置
[转载自博客](http://blog.csdn.net/huang_wei_cai/article/details/52515817) 前言: Android Studio中对一个自己库进行生成操作时将会同时生成.jar与.aar文件。如下是本人测试可行的方案,需要学习的可以参考。 分别存储位置: *.jar&#x…...
新手做网站遇到的问题以及解决方案/香港百度广告
上节我们学习了容器如何访问外部网络,今天讨论另一个方向:外部网络如何访问到容器? 答案是:端口映射。 docker 可将容器对外提供服务的端口映射到 host 的某个端口,外网通过该端口访问容器。容器启动时通过-p参数映射端…...
自己可以给公司做网站吗/千锋教育的官网
2019独角兽企业重金招聘Python工程师标准>>> 场景:生产环境下,多个普通用户登录,登录后自动记录history操作到某个统一目录保存。 具体要求: 1) 每个用户登录后自动创建子目录及history记录文件ÿ…...
西宁网站制作/国外服务器免费ip地址
题目链接 判断一个图是否为强联通图,只要tarjan求出强联通分量的个数,若个数大于1则不是连通图,tarjan的模板题。 1 #include<cstdio>2 #include<cmath>3 #include<cstring>4 #include<algorithm>5 #define mem(a) m…...