【重学C语言】六、循环结构
- 基本循环结构
- `while` 循环
- `do...while` 循环
- `for` 循环
- 死循环
- 应用场景
- 循环嵌套
- 1. 处理二维数组
- 2. 打印图案或文本
- 3. 矩阵运算
- 4. 复杂逻辑和算法
- 两个简单算法
- 拆分数字
- 统计大写小写数字其他字符个数
- 跳转语句
- 1. `break` 语句
- 2. `continue` 语句
- 3. `goto` 语句(不推荐使用)
基本循环结构
while
循环
while
循环是一种控制结构,它重复执行一段代码,只要给定的条件为真(非零)。一旦条件变为假(零),循环就停止执行。
while
循环的基本语法如下:
while (condition) {// 循环体:要重复执行的代码
}
这里,condition
是一个表达式,每次循环迭代前都会评估这个表达式。如果condition
的值为真(非零),那么循环体内的代码就会执行。如果condition
的值为假(零),那么循环就会终止,程序会继续执行while
循环之后的代码。
下面是一个简单的例子,展示了如何使用while
循环来打印从1到5的数字:
#include <stdio.h>int main() {int i = 1;while (i <= 5) {printf("%d\n", i);i++; // 更新循环变量}return 0;
}
在这个例子中,我们初始化了一个变量i
为1,然后使用while
循环来检查i
是否小于或等于5。如果是,我们就打印i
的值,并将i
增加1。这样,每次循环迭代时,i
的值都会增加,直到它不再小于或等于5,循环就会终止。
注意,在使用while
循环时,确保循环有一个明确的终止条件,否则你可能会创建一个无限循环,这会导致程序永远运行下去,直到系统资源耗尽。此外,通常还需要在循环体内更新循环条件中使用的变量,以确保循环最终会终止。
do...while
循环
do-while循环与while循环类似,但有一个关键的区别:do-while循环至少会执行一次循环体,然后再检查条件。这意味着,即使条件在第一次检查时就为假,do-while循环体中的代码也至少会被执行一次。
do-while循环的基本语法如下:
do { // 循环体:要重复执行的代码
} while (condition);
这里,condition是一个表达式,在每次循环迭代结束后都会评估这个表达式。如果condition的值为真(非零),那么循环体内的代码会再次执行。如果condition的值为假(零),那么循环就会终止,程序会继续执行do-while循环之后的代码。
下面是一个简单的例子,展示了如何使用do-while循环来打印从1到5的数字:
#include <stdio.h> int main() { int i = 1; do { printf("%d\n", i); i++; // 更新循环变量 } while (i <= 5); return 0;
}
在这个例子中,我们初始化了一个变量i为1,然后使用do-while循环来打印i的值,并将i增加1。与while循环不同,即使i的初始值就大于5,do-while循环体中的代码也至少会被执行一次(在这个例子中,这种情况不会发生,因为i的初始值是1)。然后,每次循环迭代结束后,都会检查i是否小于或等于5。如果是,循环继续;如果不是,循环终止。
请注意,do-while循环末尾的分号(;
)是必须的,它标志着循环语句的结束。同时,确保循环体中有适当的更新逻辑,以便循环条件最终能够变为假,从而避免无限循环。
for
循环
for
循环是一种非常常用的控制结构,用于重复执行一段代码块指定的次数。for
循环提供了一个简洁的方式来初始化循环变量、检查循环条件和更新循环变量。
for
循环的基本语法如下:
for (initialization; condition; update) {// 循环体:要重复执行的代码
}
initialization
:在循环开始之前执行的初始化表达式,不仅仅是简单表达式,通常用于设置循环变量的初始值。condition
:在每次循环迭代之前检查的表达式。如果条件为真(非零),则执行循环体;如果为假(零),则终止循环。update
:在每次循环迭代之后执行的表达式,通常用于更新循环变量。
下面是一个简单的例子,展示了如何使用for
循环来打印从1到5的数字:
#include <stdio.h>int main() {int i;for (i = 1; i <= 5; i++) {printf("%d\n", i);}return 0;
}
在这个例子中,我们初始化了一个变量i
为1,然后在for
循环中检查i
是否小于或等于5。如果是,就打印i
的值,并将i
增加1(通过i++
)。这个过程会一直重复,直到i
不再小于或等于5,循环就会终止。
for
循环的初始化、条件和更新部分都是可选的,但通常情况下,你会看到所有这些部分都被使用。这允许你在一个紧凑的结构中控制循环的各个方面。
下面是一个更复杂的例子,展示了for
循环的所有部分都可以如何使用:
#include <stdio.h>int main() {int i, sum = 0;for (i = 1, sum = 0; i <= 10; i++, sum += i) {// 循环体为空,因为所有的工作都在初始化和更新部分完成}printf("Sum of numbers from 1 to 10: %d\n", sum);return 0;
}
在这个例子中,我们在初始化部分同时初始化了两个变量i
和sum
。在条件部分,我们检查i
是否小于或等于10。在更新部分,我们同时增加了i
的值和sum
的值(通过将i
的值加到sum
上)。循环体是空的,因为所有的计算都在初始化和更新部分完成了。最后,我们打印出1到10之间所有数字的和。
死循环
死循环是一个无限循环,即循环永远不会自然终止,除非被外部因素(如用户中断或程序崩溃)所打断。这通常是由于循环条件永远为真,或者循环体内没有包含任何能改变循环条件的代码。
以下是一个死循环的例子:
#include <stdio.h>int main() {while (1) { // 这里的条件永远为真// 循环体:这里的代码会无限次地重复执行printf("This is an infinite loop.\n");}return 0; // 这行代码实际上永远不会被执行到,因为循环永远不会结束
}
在这个例子中,while
循环的条件是常量表达式1
,它永远为真(非零)。因此,循环体会无限次地执行,打印出消息"This is an infinite loop."。由于循环条件永远不会变为假,所以return 0;
语句永远不会被执行到。
另一个常见的死循环例子是使用for
循环:
#include <stdio.h>int main() {for (;;) { // 这里没有条件,所以默认为真// 循环体:这里的代码会无限次地重复执行printf("This is also an infinite loop.\n");}return 0; // 这行代码同样永远不会被执行到
}
在这个例子中,for
循环没有提供任何条件,这意味着循环条件默认为真。因此,这也是一个死循环。
在实际编程中,死循环通常是不希望出现的,因为它们会导致程序无法正常终止,消耗系统资源,甚至可能导致系统崩溃。因此,在编写循环时,应确保循环条件能够在某一点变为假,以便循环能够自然终止。如果确实需要实现某种形式的永久运行(例如服务器程序),则应提供外部机制(如信号处理)来安全地停止程序。
应用场景
-
重复执行特定任务:
当需要重复执行某个任务多次时,可以使用循环结构。例如,计算一个数的累加和、打印某个模式的多个副本,或者重复读取用户输入直到满足特定条件。 -
遍历数组或集合:
对于数组、链表或其他集合类型的数据结构,经常需要遍历其中的每个元素以执行某种操作。循环结构可以方便地实现这一点。 -
数值计算:
在科学计算、物理模拟或其他数值计算任务中,经常需要执行大量的重复计算。循环结构使得这些计算变得高效且易于管理。 -
文件处理:
当处理大量数据时,例如从文件中读取或写入数据,循环结构可以帮助逐行或逐个元素地处理数据。 -
游戏和图形编程:
在编写游戏或图形程序时,循环结构常用于更新游戏状态、渲染图形或处理用户输入。 -
事件驱动编程:
在某些情况下,程序可能需要根据特定事件(如定时器事件、中断或用户输入)重复执行代码。循环结构可以与事件处理机制结合使用,以实现这一功能。 -
算法实现:
许多算法(如排序、搜索、递归等)都涉及到重复执行某些步骤,这些步骤通常通过循环结构来实现。 -
模拟和仿真:
在模拟或仿真系统中,可能需要模拟一系列时间步长或迭代来观察系统的行为。循环结构使得这种模拟变得简单且高效。
下面是一个简单的例子,展示了如何在C语言中使用for
循环来计算一个数的阶乘:
#include <stdio.h>int main() {int n, factorial = 1;printf("Enter a positive integer: ");scanf("%d", &n);for (int i = 1; i <= n; i++) {factorial *= i;}printf("Factorial of %d is %d\n", n, factorial);return 0;
}
在这个例子中,用户输入一个正整数n
,程序使用一个for
循环来计算n
的阶乘(即n!
),并将结果打印出来。这个循环从1迭代到n
,并在每次迭代中将当前的循环变量i
乘以factorial
。最终,factorial
变量将包含n
的阶乘。
循环嵌套
循环嵌套是编程中一种常见的结构,指的是在一个循环体内部又包含另一个或多个循环。可以使用while
、do-while
或for
循环来创建嵌套循环。嵌套循环在处理二维数组、矩阵运算、打印图案或执行需要多次迭代的复杂任务时非常有用。
1. 处理二维数组
当你需要遍历一个二维数组(例如矩阵)时,通常会使用嵌套循环。外层循环遍历行,内层循环遍历列。
#include <stdio.h>int main() {int matrix[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};int i, j;for (i = 0; i < 3; i++) {for (j = 0; j < 3; j++) {printf("%d ", matrix[i][j]);}printf("\n"); // 换行,以便每行数据单独显示}return 0;
}
2. 打印图案或文本
嵌套循环常用于在控制台上打印出特定的图案或文本格式。
#include <stdio.h>int main() {int i, j, n = 5;for (i = 1; i <= n; i++) {for (j = 1; j <= i; j++) {printf("* ");}printf("\n");}for (i = n - 1; i >= 1; i--) {for (j = 1; j <= i; j++) {printf("* ");}printf("\n");}return 0;
}
这段代码会打印出一个由星号组成的菱形图案。
3. 矩阵运算
嵌套循环常用于执行矩阵加法、乘法或其他运算。
#include <stdio.h>#define SIZE 3void matrixAddition(int a[SIZE][SIZE], int b[SIZE][SIZE], int result[SIZE][SIZE]) {int i, j;for (i = 0; i < SIZE; i++) {for (j = 0; j < SIZE; j++) {result[i][j] = a[i][j] + b[i][j];}}
}int main() {int matrixA[SIZE][SIZE] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};int matrixB[SIZE][SIZE] = {{9, 8, 7}, {6, 5, 4}, {3, 2, 1}};int result[SIZE][SIZE];int i, j;matrixAddition(matrixA, matrixB, result);for (i = 0; i < SIZE; i++) {for (j = 0; j < SIZE; j++) {printf("%d ", result[i][j]);}printf("\n");}return 0;
}
这段代码定义了一个函数matrixAddition
,它接受两个矩阵作为输入,并返回它们的和。主函数main
中初始化了两个矩阵,调用了matrixAddition
函数,并打印出结果矩阵。
4. 复杂逻辑和算法
嵌套循环还常用于实现更复杂的逻辑和算法,例如搜索、排序或其他涉及多维数据结构的操作。
在使用嵌套循环时,务必注意循环的终止条件,以避免无限循环或意外的行为。同时,优化嵌套循环的性能也是编程中的一个重要考虑因素,特别是在处理大量数据时。
两个简单算法
拆分数字
如果你想要拆分一个数字(例如,将一个整数拆分为其各个位上的数字),你可以通过不断地除以10并取余数来实现。以下是一个简单的示例,它演示了如何将一个整数拆分为其个位、十位、百位等上的数字:
#include <stdio.h>int main() {int number, originalNumber, remainder, position = 1;printf("请输入一个整数:");scanf("%d", &number);originalNumber = number; // 保存原始数字以便后续输出printf("数字的各位分别为:\n");while (number != 0) {remainder = number % 10; // 取最后一位数字printf("第%d位:%d\n", position, remainder);number /= 10; // 去掉最后一位数字position++; // 增加位数}printf("原始数字是:%d\n", originalNumber);return 0;
}
在这个程序中,我们首先让用户输入一个整数。然后,我们使用一个while
循环来不断地除以10并取余数,直到数字变为0。在每次循环中,我们都打印出当前位上的数字,并增加位数计数器。最后,我们打印出原始的输入数字。
例如,如果用户输入数字12345
,程序将输出:
请输入一个整数:12345
数字的各位分别为:
第1位:5
第2位:4
第3位:3
第4位:2
第5位:1
原始数字是:12345
注意,这个程序假设用户输入的是一个正整数。如果用户输入了0或负数,程序仍然可以工作,但输出可能会有些不同(例如,对于0,不会有任何位被打印出来)。如果需要处理负数或0的特殊情况,你需要在代码中添加额外的逻辑来处理这些情况。
统计大写小写数字其他字符个数
不使用数组、字符串、函数和指针来统计大写字母、小写字母、数字和其他字符的个数是一项挑战,因为通常字符串操作都是通过指针和数组进行的。但是,如果我们假设输入是通过逐个字符的方式提供的,而不是作为字符串,那么我们可以使用变量来直接处理每个输入的字符。
以下是一个简单的示例,该示例通过逐个字符的输入来统计大写字母、小写字母、数字和其他字符的个数:
#include <stdio.h>int main() {char ch;int uppercase = 0, lowercase = 0, digit = 0, other = 0;printf("请输入字符(输入'q'退出):\n");// 循环读取字符直到输入'q'while (1) {ch = getchar(); // 从标准输入读取一个字符if (ch == 'q') { // 如果输入是'q',则退出循环break;}if (ch >= 'A' && ch <= 'Z') { // 大写字母uppercase++;} else if (ch >= 'a' && ch <= 'z') { // 小写字母lowercase++;} else if (ch >= '0' && ch <= '9') { // 数字digit++;} else { // 其他字符other++;}}// 打印统计结果printf("大写字母个数: %d\n", uppercase);printf("小写字母个数: %d\n", lowercase);printf("数字个数: %d\n", digit);printf("其他字符个数: %d\n", other);return 0;
}
在这个程序中,我们使用getchar()
函数从标准输入读取一个字符,然后检查该字符是否是大写字母、小写字母、数字或其他字符,并相应地增加计数器。当用户输入字符’q’时,程序退出循环并打印统计结果。
需要注意的是,这个示例仅适用于从标准输入逐个读取字符的情况。如果你有一个预先定义好的字符串并且不能使用指针或数组来访问它,那么在不使用这些工具的情况下进行字符统计是不可能的,因为你需要某种方式来遍历字符串中的字符。在C语言中,遍历字符串通常是通过指针或数组索引实现的。
跳转语句
跳转语句用于改变程序的正常执行流程,即跳过某些语句或提前结束循环。这些跳转语句主要包括break
、continue
和goto
。
1. break
语句
break
语句用于提前终止循环或switch
语句的执行。当break
语句被执行时,程序会立即跳出包含它的最内层循环或switch
语句,并继续执行后面的语句。
示例:
#include <stdio.h>int main() {int i;for (i = 0; i < 10; i++) {if (i == 5) {break; // 当 i 等于 5 时,跳出循环}printf("%d ", i);}return 0;
}
在这个例子中,当i
等于5时,break
语句会被执行,循环会被提前终止,因此输出只会是0 1 2 3 4
。
2. continue
语句
continue
语句用于跳过循环体中剩余的语句,并立即开始下一次循环迭代。
示例:
#include <stdio.h>int main() {int i;for (i = 0; i < 10; i++) {if (i == 5) {continue; // 当 i 等于 5 时,跳过本次循环的剩余部分}printf("%d ", i);}return 0;
}
在这个例子中,当i
等于5时,continue
语句会被执行,跳过printf
语句,直接开始下一次循环迭代。因此,输出会是0 1 2 3 4 6 7 8 9
。
3. goto
语句(不推荐使用)
goto
语句用于无条件地跳转到程序中标记的位置。尽管goto
在某些情况下可能有用,但通常不建议使用它,因为它会使程序流程变得难以理解和维护。现代编程风格更倾向于使用结构化编程方法,如循环和条件语句,来组织代码。
示例(尽管不推荐使用):
#include <stdio.h>int main() {int i = 0;if (i == 0) {goto skip; // 跳转到标记为 skip 的位置}printf("This will not be printed.\n");
skip:printf("This will be printed.\n");return 0;
}
在这个例子中,goto
语句会使程序跳过printf("This will not be printed.\n");
语句,直接跳转到标记为skip
的位置,并输出This will be printed.
。
总的来说,break
和continue
是循环结构中常用的跳转语句,而goto
则应当谨慎使用,并尽量避免在复杂的程序中使用。