C语言 循环——《跟老吕学C》
C语言 循环
在C语言中,循环结构是编程的基石之一,它赋予程序重复执行特定代码段的能力,直至满足特定的条件。C语言为我们提供了三种主要的循环结构:for循环、while循环、do-while循环和嵌套循环。接下来,我们将深入探讨这四种循环结构,并给出相应的示例。
循环类型 | 描述 |
---|---|
while 循环 | 当给定的条件为真时,重复执行一组语句。该循环会在执行循环主体之前先测试条件。 |
for 循环 | 它用于多次执行一个语句序列,并通过简化管理循环变量的代码来优化循环过程。 |
do…while 循环 | 与 while 循环类似,但主要区别在于它是在循环体执行完毕后再测试条件。 |
嵌套循环 | 嵌套循环允许在 while、for 或 do…while 循环的内部使用一个或多个其他循环,从而形成嵌套结构。 |
一、for循环
for循环因其简洁和直观性,成为了编程中最常用的循环结构之一。它的语法结构如下:
for (初始化表达式; 条件表达式; 更新表达式) {
// 循环体
}
在c语言for循环语句用法中,首先执行一次初始化表达式,这通常用于初始化循环控制变量。然后,程序会检查条件表达式,如果为真,则执行循环体中的代码。在每次循环迭代结束时,都会执行更新表达式,以更新循环控制变量。这个过程会一直重复,直到条件表达式为假。
以下是一个使用for循环的示例,它打印出从0到9的整数:
#include <stdio.h>
int main() {
int i;
printf("开始打印从0到9的整数:\n");
for (i = 0; i < 10; i++) {
printf("%d ", i);
}
printf("\n打印结束。\n");
return 0;
}
以下是一个使用C语言for循环打印菱形示例:
#include <stdio.h>
int main() {
int n, i, j, k;
// 假设菱形的高度为2n-1
printf("请输入菱形的高度(奇数): ");
scanf("%d", &n);
// 打印上半部分(包括中间行)
for (i = 1; i <= n; i++) {
// 打印空格
for (j = 1; j <= n - i; j++) {
printf(" ");
}
// 打印星号
for (k = 1; k <= 2 * i - 1; k++) {
printf("*");
}
// 换行
printf("\n");
}
// 打印下半部分(不包括中间行)
for (i = n - 1; i >= 1; i--) {
// 打印空格
for (j = 1; j <= n - i; j++) {
printf(" ");
}
// 打印星号
for (k = 1; k <= 2 * i - 1; k++) {
printf("*");
}
// 换行
printf("\n");
}
return 0;
}
在这个示例中,我们首先定义了一个整数n
,它表示菱形高度的一半(因为我们希望菱形的行数为奇数,所以总高度为2n-1
)。然后,我们使用两个嵌套的for循环来分别打印菱形的上半部分和下半部分。
对于上半部分,我们首先打印一定数量的空格(数量由n-i
决定),然后打印相应数量的星号(数量由2*i-1
决定)。每打印完一行后,我们进行换行。
对于下半部分,我们采用类似的方法,但是注意这里i
的值是从n-1
递减到1
的。这是为了确保菱形是对称的。
当运行这段代码并输入一个奇数作为菱形的高度时,它会打印出一个由星号组成的菱形。
二、while循环
while循环的语法结构相对简单,如下所示:
while (条件表达式) {
// 循环体
}
在while循环中,程序会首先检查条件表达式。如果条件为真,则执行循环体中的代码。然后,程序会再次检查条件表达式,如此往复,直到条件为假。
下面是一个使用while循环的示例,其功能与上面的for循环示例相同:
#include <stdio.h>
int main() {
int i = 0;
printf("开始打印从0到9的整数:\n");
while (i < 10) {
printf("%d ", i);
i++; // 注意这里需要手动增加i的值,否则会导致无限循环
}
printf("\n打印结束。\n");
return 0;
}
在这个示例中,我们需要注意在循环体内部增加i的值,否则由于条件表达式始终为真,会导致无限循环。
三、do-while循环
do-while循环与while循环类似,但有一个重要的区别:do-while循环至少会执行一次循环体,然后再检查条件表达式。其语法结构如下:
do {
// 循环体
} while (条件表达式);
在do-while循环中,无论条件表达式是否为真,循环体都会至少执行一次。然后,程序会检查条件表达式。如果条件为真,则继续执行循环体;如果为假,则退出循环。
以下是一个使用do-while循环的示例:
#include <stdio.h>
int main() {
int i = 0;
printf("开始打印从0到9的整数:\n");
do {
printf("%d ", i);
i++;
} while (i < 10);
printf("\n打印结束。\n");
return 0;
}
这个示例的功能与前两个示例相同,但使用了do-while循环来实现。在这个示例中,我们不需要担心无限循环的问题,因为无论条件表达式是否为真,循环体都会至少执行一次。
四、嵌套循环
在C语言中,循环结构是一个强大的工具,它们不仅可以单独使用,还可以嵌套使用。所谓嵌套循环,就是指在一个循环体内部,再次使用循环结构,形成循环的嵌套关系。这种嵌套循环的方式在处理二维数组、打印特定图案等任务时,显得尤为重要。
以打印九九乘法表为例,我们可以使用两个嵌套的for
循环来实现。外层循环控制行数,内层循环控制列数,两者共同合作,打印出完整的乘法表。
#include <stdio.h>
int main() {
int i, j;
for (i = 1; i <= 9; i++) {
printf("第%d行:\t", i); // 添加行标题
for (j = 1; j <= i; j++) {
printf("%d*%d=%d\t", j, i, i*j);
}
printf("\n"); // 换行
}
return 0;
}
在上面的代码中,我们为每一行添加了一个行标题,使得输出的乘法表更加清晰易读。
五、循环控制语句
在循环执行过程中,我们有时可能需要提前结束循环或跳过当前迭代。这时,C语言为我们提供了break
和continue
两个关键字,它们为控制循环流程提供了极大的灵活性。然而,除了这两个关键字外,C语言还提供了goto
语句,尽管在现代编程实践中,goto
的使用通常被认为是不良的编程习惯,但在某些特殊情况下,它仍然有其用途。
控制语句 | 描述 |
---|---|
break 语句 | 用于终止循环或switch语句。程序流将继续执行紧接着该循环或switch语句之后的下一条语句。 |
continue 语句 | 用于使循环跳过当前迭代的剩余部分,并立即开始下一次迭代的条件测试。 |
goto 语句 | 用于将程序的控制流转移到被标记的语句。然而,由于goto语句可能会降低代码的可读性和可维护性,因此在大多数编程实践中并不推荐使用它。 |
break
语句:当在循环体中遇到break
语句时,程序会立即退出最内层的循环。这种机制在需要提前终止循环时非常有用,例如,在查找一个特定的数字时,一旦找到,我们就可以使用break
语句提前结束循环。
#include <stdio.h>
int main() {
int i, num = 5;
for (i = 1; i <= 10; i++) {
if (i == num) {
printf("在循环中找到了数字 %d\n", num);
break; // 提前结束循环
}
}
printf("循环已结束。\n"); // 循环结束后打印提示信息
return 0;
}
continue
语句:当在循环体中遇到continue
语句时,程序会跳过当前迭代的剩余部分,直接进入下一次迭代。这种机制在处理需要忽略某些迭代条件的循环时特别有用,例如,在打印1到10之间的所有奇数时,我们可以使用continue
语句跳过偶数。
#include <stdio.h>
int main() {
int i;
for (i = 1; i <= 10; i++) {
if (i % 2 == 0) {
continue; // 跳过偶数
}
printf("%d ", i); // 打印奇数
}
printf("\n"); // 换行
return 0;
}
goto
语句:虽然goto
语句在现代编程中被视为应该避免使用的“有害”结构,但它确实在某些特定的、复杂的控制流场景中展示出其用途。goto
语句允许程序无条件地跳转到指定的标签位置,这种能力在某些情况下能够简化复杂的流程控制。然而,滥用goto
语句可能会导致代码结构混乱,因此在使用时需要格外小心。
一个使用goto
语句的简单示例:
在C语言中,我们可以使用goto
语句来处理嵌套循环中的错误情况。假设我们有一个两层嵌套的循环,需要在某个条件触发时立即跳出循环并处理错误。不使用goto
语句,我们可能需要设置多个标志变量或使用复杂的break
语句组合,但使用goto
可以更加直接。
#include <stdio.h>
int main() {
int i, j;
int errorOccurred = 0;
for (i = 0; i < 10; i++) {
for (j = 0; j < 10; j++) {
if (/* some error condition */) {
errorOccurred = 1;
goto errorHandler;
}
// 正常处理逻辑
}
}
// 如果没有错误发生,则执行后续代码
// ...
// 错误处理代码块
errorHandler:
if (errorOccurred) {
printf("An error occurred!\n");
// 错误处理逻辑
}
return 0;
}
在这个示例中,当满足某个错误条件时,我们设置errorOccurred
为1,并使用goto
语句无条件地跳转到errorHandler
标签位置。然后,在errorHandler
标签下方,我们检查errorOccurred
变量,并根据需要执行错误处理逻辑。
然而,正如之前提到的,这样的用法在现代编程实践中并不常见,因为它可能会降低代码的可读性和可维护性。在许多情况下,我们可以重新组织代码结构,使用函数、条件语句和循环语句来更好地控制程序流程。
结构化编程技术鼓励我们将代码划分为可管理的块,并使用函数、条件语句和循环语句来组织这些块。这种方法使得代码更加清晰、易于理解和维护。因此,在编写代码时,我们应该尽量避免使用goto
语句,而是优先考虑使用结构化编程技术来组织我们的代码。
六、循环的效率和优化
在编写循环时,我们不仅要关注其正确性,还要考虑其效率。一个不合理的循环结构可能会导致程序运行缓慢或占用过多资源。以下是一些关于循环优化的建议:
- 尽量减少循环体中的计算量。对于可以在循环外部完成的计算,应该提前进行,避免在每次迭代中都重复计算。
- 对于需要处理大量数据的情况,我们应该考虑使用更有效的算法或数据结构来减少循环的次数或降低每次迭代的计算复杂度。
- 避免在循环中频繁调用函数或进行内存分配操作。这些操作通常比较耗时,并且可能导致额外的内存开销。
- 对于嵌套循环,我们应该尽量减少内层循环的次数或避免不必要的内层循环。例如,在遍历二维数组时,我们可以先判断数组边界,避免不必要的内层循环。
通过以上优化措施,我们可以提高程序的运行效率,使其更加高效地完成各项任务。
七、C语言循环题目经典题
1. 鸡兔同笼c语言for循环
鸡兔同笼是一个经典的数学问题,问题大致描述为:一个笼子里有若干只鸡和兔,从上面数,有n个头;从下面数,有m只脚。问笼子里有多少只鸡和多少只兔?
假设笼子里有x
只鸡和y
只兔,那么根据题目描述,我们可以得到以下方程:
- 头的总数:
x + y = n
- 脚的总数:
2x + 4y = m
(因为鸡有2只脚,兔有4只脚)
由于这是一个有两个未知数的方程组,我们可以使用代数方法解出x
和y
,但在这里,我们将使用C语言的for
循环来遍历可能的解,找出满足条件的x
和y
。
以下是一个使用C语言for
循环解决鸡兔同笼问题的示例代码:
#include <stdio.h>
int main() {
int n, m;
printf("请输入头的数量n和脚的数量m(例如,输入10 26表示有10个头和26只脚):");
scanf("%d %d", &n, &m);
for (int x = 0; x <= n; x++) {
int y = n - x; // 根据第一个方程解出y
if (2*x + 4*y == m) {
// 检查是否满足第二个方程
printf("笼子里有%d只鸡和%d只兔。\n", x, y);
break; // 找到解后退出循环
}
}
if (x > n) {
printf("没有满足条件的解。\n");
}
return 0;
}
这段代码首先通过scanf
函数获取用户输入的头和脚的数量。然后,使用一个for
循环遍历所有可能的鸡的数量(从0到n)。对于每个可能的鸡的数量,我们根据第一个方程计算出兔的数量,并检查是否满足第二个方程。如果找到满足条件的解,就打印出来并退出循环。如果没有找到满足条件的解,就打印一条消息说明没有解。
2. 韩信点兵c语言while循环
韩信点兵是另一个经典的数学问题,又被称为“中国剩余定理”的雏形。问题的大致描述是:韩信有一队士兵,他让士兵们站成三列,结果多出两人;再命令士兵站成五列,结果多出三人;站成七列,结果多出两人。问韩信至少有多少名士兵?
为了解决这个问题,我们可以使用C语言的while
循环来遍历可能的士兵数量,直到找到满足所有条件的最小数量。
以下是一个使用C语言while
循环解决韩信点兵问题的示例代码:
#include <stdio.h>
int main() {
int soldiers = 0; // 初始化士兵数量为0
int found = 0; // 标记是否找到解
// 使用while循环遍历可能的士兵数量
while (!found) {
soldiers++; // 每次循环士兵数量加1
// 检查是否满足所有条件
if (soldiers % 3 == 2 && soldiers % 5 == 3 && soldiers % 7 == 2) {
found = 1; // 找到解,设置标记为1
}
}
// 打印结果
printf("韩信至少有%d名士兵。\n", soldiers);
return 0;
}
这段代码使用一个while
循环来遍历所有可能的士兵数量。在每次循环中,我们将士兵数量加1,并检查是否满足所有条件(即站成三列多出两人,站成五列多出三人,站成七列多出两人)。如果找到满足条件的解,我们就设置标记found
为1,并在循环结束后打印结果。
通过这种方法,我们可以使用C语言的循环结构来解决这类经典的数学问题。无论是使用for
循环还是while
循环,关键在于理解问题的本质,并设计合适的循环结构和条件判断来找到满足条件的解。
八、无限循环
在C语言编程中,如果某个条件永远成立,那么相应的循环将陷入无限循环的状态。传统上,for循环结构被用来实现这一目的,因为构成for循环的三个部分(初始化、条件判断、循环后操作)中的任何一个都不是必须的。例如,你可以通过省略或设置条件判断部分始终为真的表达式来构造一个无限循环。然而,在实际编程实践中,无限循环通常是不被推荐的,因为它们可能导致程序无响应或过度消耗系统资源。
尽管如此,在某些情况下,无限循环是必需的,例如用于实时数据监控、游戏循环等。在这些场景中,我们通常使用其他机制(如事件监听器或定时器)来在必要时中断循环。
以下是一个简单的示例,展示了如何使用for循环创建一个无限循环(注意:此示例仅用于教学目的,实际编程中应避免使用):
#include <stdio.h>
int main() {
for (;;) {
// 无限循环,条件表达式为空
printf("This will run forever unless interrupted manually.\n");
}
return 0; // 注意:这行代码实际上永远不会被执行到
}
在上面的示例中,for循环的条件表达式被省略了(使用两个分号表示),这意味着循环将永远执行下去,除非被外部因素(如用户中断)打断。
为了避免无限循环带来的问题,我们应该在循环体内部添加适当的退出条件。这可以通过使用if语句检查某个条件并在满足条件时使用break语句来跳出循环来实现。
#include <stdio.h>
int main() {
int count = 0;
for (;;) {
// 无限循环,但包含退出条件
printf("Count: %d\n", count);
count++;
if (count >= 10) {
// 当count达到10时退出循环
break;
}
}
return 0;
}
在上面的示例中,我们添加了一个计数变量count,并在每次迭代时增加其值。当count达到10时,我们使用break语句跳出循环。这样,循环就不会无限执行下去,而是会在满足条件时正常结束。
总结
循环是C语言中不可或缺的部分,它让我们能够重复执行某段代码,直至满足特定条件。在编写循环时,我们应关注其效率,并合理运用循环控制语句来控制其执行。同时,必须警惕无限循环的编写,以免程序无法响应或崩溃。通过深入理解for循环、while循环、do-while循环、嵌套循环以及循环控制语句等基本概念和技巧,我们能够创建出更为高效、稳定的C语言程序。
-
for
循环适用于已知循环次数的情况,因为它允许在初始化时直接设定循环控制变量的初始值,并在每次迭代后更新该值。 -
当循环次数不确定时,
while
循环和do-while
循环更为适用。while
循环在条件表达式为假时不会执行循环体,而do-while
循环则至少会执行一次循环体,无论条件表达式如何。 -
嵌套循环是循环结构中的重要概念,它允许我们在一个循环内部再嵌套另一个或多个循环。这在处理多维数组、矩阵运算、图形绘制等复杂任务时尤为有用。在编写嵌套循环时,应特别注意循环变量的命名和初始化,以避免混淆和错误。
-
除了基本的循环结构外,C语言还提供了多种循环控制语句,如
break
、continue
和goto
等。break
语句用于立即终止最内层循环的执行;continue
语句则跳过当前循环的剩余部分,直接进入下一次迭代;而goto
语句可实现无条件跳转,但通常不推荐使用,因为它可能会破坏程序的结构和可读性。 -
在编写循环时,我们还需考虑循环的性能优化。常见的优化技巧包括减少循环体内的计算量、避免不必要的函数调用、采用更高效的算法和数据结构等。此外,编译器也会自动进行一些循环优化,如循环展开、公共子表达式消除等,以提升程序的执行效率。
-
注意事项:编写循环时,务必警惕常见的错误和陷阱。例如,死循环和无限循环可能导致程序无法正常终止。为避免这些错误,需仔细检查循环条件和控制变量的更新方式。同时,还需注意循环变量的作用域和生命周期,以避免意外的副作用和错误。
循环结构是C语言编程中的关键组成部分,它使我们能够编写高效、简洁的代码来处理重复任务。通过熟练掌握for
循环、while
循环、do-while
循环的基本用法以及嵌套循环、循环控制、循环优化等高级技巧,我们能够编写出更加健壮、高效的C语言程序。同时,还需时刻警惕常见的错误和陷阱,并采取相应的措施来避免它们的发生。