C 语言基础 4 - 循环结构
引言
在高级语言编程中,特别是C语言编程,使用循环结构的主要原因是为了提高代码的可读性、可维护性和执行效率。循环结构允许我们重复执行某段代码,而不必每次都手动编写相同的代码块。这在处理 重复任务
时特别有用,比如遍历数组、处理文件中的数据、进行数值计算等。
下面通过几个例子来引入循环结构:
例1:打印数字1到10
不使用循环结构:
#include <stdio.h>
int main() {
printf("1\n");
printf("2\n");
printf("3\n");
printf("4\n");
printf("5\n");
printf("6\n");
printf("7\n");
printf("8\n");
printf("9\n");
printf("10\n");
return 0;
}
使用循环结构:
#include <stdio.h>
int main() {
for (int i = 1; i <= 10; i++) {
printf("%d\n", i);
}
return 0;
}
例2:计算1到100的和
不使用循环结构(几乎不可能手动写出所有相加的操作):
#include <stdio.h>
int main() {
int sum;
sum = 1 + 2 + 3 + ... + 100; // 不现实,因为需要写100行代码来相加
printf("Sum: %d\n", sum);
return 0;
}
使用循环结构:
#include <stdio.h>
int main() {
int sum = 0;
for (int i = 1; i <= 100; i++) {
sum += i;
}
printf("Sum: %d\n", sum);
return 0;
}
例3:遍历数组并打印每个元素
不使用循环结构(对于固定大小的数组,但非常不灵活):
#include <stdio.h>
int main() {
int arr[] = {1, 2, 3, 4, 5};
printf("%d\n", arr[0]);
printf("%d\n", arr[1]);
printf("%d\n", arr[2]);
printf("%d\n", arr[3]);
printf("%d\n", arr[4]);
return 0;
}
使用循环结构:
#include <stdio.h>
int main() {
int arr[] = {1, 2, 3, 4, 5};
int length = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < length; i++) {
printf("%d\n", arr[i]);
}
return 0;
}
总结
- 提高代码可读性:循环结构使得代码更加简洁,易于理解。
- 提高代码可维护性:如果需要修改循环次数或逻辑,只需更改循环控制语句,而不必修改多行代码。
- 提高执行效率:循环结构避免了重复编写相同的代码,减少了内存占用和提高了执行速度。
通过上面的例子可以看出,循环结构在C语言编程中非常重要,它极大地提高了编程的效率和灵活性。
注意事项
- 在使用循环结构时,务必确保循环条件最终会变为假,以避免无限循环。
- 可以通过
break
语句提前退出循环,通过continue
语句跳过当前迭代并继续下一次迭代。 - 在使用
for
循环时,初始化表达式、循环条件和迭代表达式都是可选的,但省略它们可能会导致不可预测的行为。
三种循环语句
for循环
-
for
循环是最常用的循环结构之一,它通常用于已知循环次数的场景。for(初始表达式; 循环条件; 迭代表达式) { 循环体 }
循环的语法如下: -
初始化表达式: 在循环开始前执行,通常用于初始化循环控制变量。
-
循环条件: 在每次循环迭代前检查,如果条件为真(非零),则执行循环体;如果条件为假(零),则退出循环。
-
迭代表达式: 在每次循环迭代结束时执行,通常用于更新循环控制变量。
-
语法:
for (初始化表达式; 循环条件; 迭代表达式) { // 循环体 }
-
执行流程:
-
计算初始表达式(只执行一次)
-
判断循环条件,若为真则执行循环体,否则跳出循环
-
执行循环体
-
计算迭代表达式
-
重复步骤 b
-
-
示例:计算1到20的和
#include <stdio.h>
void main() {
long int result = 0;
for (int i = 1; i <= 20; i++) {
result += i; //即result=result + i;
printf("和 = %ld\n", result);
}
}
- 注意事项:
- 循环体如果有一条以上的语句,应该用
大括号{}
括起来,如果只有一条语句,大括号
可以省略
。 - for语句中的表达式可以省略任意一个,也可以都省略,但“
;
”不能省略。 需要特别注意是“;
”会经常错误的写成逗号“,
” ,或者在循环语句的小括号外面加“;
”。
- 循环体如果有一条以上的语句,应该用
示例:计算1到20的和
#include <stdio.h>
void main() {
long int result = 0;
int i=1;//如果在外面定于了循环语句的初始值,或者其他特殊情况,循环语句可以省略
for (; i <= 20; i++) //{
result += i; //即result=result + i;
//}//如果循环体中只有一条语句,大括号可以省略,为了可读性,建议无论何时都添加大括号
printf("和 = %ld\n", result);
}
while循环
while
循环是一种基本的循环控制结构,用于在给定条件为真的情况下重复执行一段代码。当循环次数未知
时,while
循环特别有用,因为你可以根据某个条件(而不是一个固定的计数器)来控制循环的执行。while
与for
循环不同,while
循环没有初始化表达式和迭代表达式,这些操作需要在循环外部进行循环的语法如下:- **循环条件:**在每次循环迭代前检查,如果条件为真(非零),则执行循环体;如果条件为假(零),则退出循环。
- 语法:
while(循环条件) { 循环体 }
while (循环条件) {
// 循环体
}
- 执行流程:
- 判断循环条件,若为真则执行循环体,否则跳出循环
- 执行循环体
- 重复步骤 a
- 注意:
- 循环体中必须有改变循环控制变量值的控制语句,否则可能导致死循环。
- 循环体如果有一条以上的语句,应该用大括号括起来,如果只有一条语句,大括号可以省略。
- 示例:
#include <stdio.h>
#include <stdbool.h>
int main() {
int number;
bool keepRunning = true;
printf("Enter numbers (enter -1 to stop):\n");
// 使用 while 循环读取用户输入,直到输入 -1
while (keepRunning) {
printf("Enter a number: ");
scanf("%d", &number);
if (number == -1) {
keepRunning = false; // 输入 -1 时停止循环
} else {
printf("You entered: %d\n", number);
}
}
printf("Loop ended. Goodbye!\n");
return 0;
}
在这个示例中:
- 我们声明了一个整型变量
number
和一个布尔变量keepRunning
。 keepRunning
初始化为true
,表示循环开始时是活动的。- 使用
while
循环,条件为keepRunning
为true
。 - 在循环体内,使用
scanf
读取用户输入的整数,并存储在number
变量中。 - 如果用户输入的是
-1
,则将keepRunning
设置为false
,从而结束循环。 - 如果用户输入的不是
-1
,则打印出用户输入的数。 - 当循环结束时,打印一条消息表示循环已结束。
这个示例展示了如何使用 while
循环来处理不确定次数的迭代,直到满足某个特定条件(在本例中是用户输入 -1
)为止。
do-while循环
do-while
循环与while
循环类似,但有一个重要的区别:do-while
循环至少会执行一次循环体,即使循环条件在第一次检查时就为假。do-while
循环的语法如下:- 循环条件:在每次循环迭代后检查,如果条件为真(非零),则重复执行循环体;如果条件为假(零),则退出循环。
- 语法:
do { 循环体 } while(循环条件);
do {
// 循环体
} while (循环条件);
- 执行流程:
- 执行循环体
- 判断循环条件,若为真则重复步骤 a,否则跳出循环
-
与while的比较:do-while循环至少执行一次循环体,而while循环可能一次也不执行。
-
示例:计算从23递减到1之前的所有数
int x = 23; do { printf("%2d\n", x--); } while (!x); // 输出:23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
选择三种循环的一般原则
- 在选择循环结构时,通常根据循环的具体需求和特点来选择适合的循环类型。以下是选择三种循环(for循环、while循环、do-while循环)的一般原则:
- for循环
适用场景:当循环次数已知或可以在循环开始前确定时,使用for循环。
特点:for循环通常包括初始化表达式、条件表达式和迭代表达式。它适合用于需要明确循环次数的场景,如遍历数组、集合等。
示例:
for (int i = 0; i < 10; i++) {
// 循环体
}
- while循环
适用场景:当循环次数未知,但循环条件可以在每次迭代前进行判断时,使用while循环。
特点:while循环在进入循环体之前先判断条件,如果条件为真,则执行循环体;否则,跳出循环。它适合用于需要根据某些条件来决定是否继续循环的场景。
示例:
int i = 0;
while (i < 10) {
// 循环体
i++;
}
- do-while循环
适用场景:当循环体至少需要执行一次,且循环条件在每次迭代后进行判断时,使用do-while循环。
特点:do-while循环先执行循环体,然后判断条件。如果条件为真,则继续循环;否则,跳出循环。它适合用于需要确保循环体至少执行一次的场景。
示例:
int i = 0;
do {
// 循环体
i++;
} while (i < 10);
- 总结
- 如果循环次数已知或可以在循环开始前确定,使用for循环。
- 如果循环次数未知,但需要根据条件来决定是否继续循环,使用while循环。
- 如果循环体至少需要执行一次,并且循环条件在每次迭代后进行判断,使用do-while循环。
- 在选择循环结构时,还需要考虑代码的可读性、维护性和效率等因素。根据具体的需求和场景,选择最合适的循环结构可以提高代码的质量和效率。
- 混合使用
当然很多时候同一函数中可以使用不同的循环结构实现,并且大多数相同任务可以使用不同循环结构实现。示例:
// 1、用while, do - while, for循环完成计算n!。要求n从键盘输入,并输出结果。
#include <stdio.h>
#include <limits.h>
int main() {
int n;
long long t;
// 输入验证
while (1) {
printf("请输入一个非负整数n的值并求它的阶乘: ");
if (scanf("%d", &n) != 1) {
printf("输入无效,请输入一个整数。\n");
// 清除输入缓冲区
while (getchar() != '\n');
continue;
}
if (n < 0) {
printf("请输入一个非负整数。\n");
continue;
}
if (n > 20) {
printf("输入的数太大,可能导致溢出,请输入一个小于等于20的数。\n");
continue;
}
break;
}
// for 循环计算阶乘
t = 1;
for (int i = 1; i <= n; i++) {
t *= i;
}
printf("for循环计算n! = %lld\n", t);
// while 循环计算阶乘
t = 1;
int i = 1;
while (i <= n) {
t *= i;
i++;
}
printf("while循环计算n! = %lld\n", t);
// do-while 循环计算阶乘
t = 1;
i = 1;
do {
t *= i;
i++;
} while (i <= n);
printf("do-while循环计算n! = %lld\n", t);
return 0;
}
循环嵌套
- 定义:一个循环体内又包含另一个完整的循环结构,称为循环的嵌套。
- 形式:可以是for、while、do-while的任意组合。
- 执行:外层循环执行一次,内层循环执行一轮(即执行完自己的循环)。
- 变量: 内层循环控制可以直接引用外层循环的相关变量,但不要轻易改变外层循环控制变量的值。
- 示例:打印二维图形
// 用*号打印三角形
#include <stdio.h>
int i, j, k;
void printTriangle(int height) {
for (i = 1; i <= height; i++) {
// 打印每行前面的空格
for (j = 0; j < height - i; j++) {
printf(" ");
}
// 打印星号
for (k = 0; k < 2 * i - 1; k++) {
printf("*");
}
// 换行
printf("\n");
}
}
int main() {
int height;
// 提示用户输入三角形的高度
printf("请输入三角形的高度: ");
scanf("%d", &height);
// 调用函数打印三角形
printTriangle(height);
return 0;
}
// 输出:
// *
// ***
// *****
// *******
//*********
编程实现 n!的倒数的和
//2、编程实现:Sn=1+1/2!+1/3!+....1/n!
#include <stdio.h>
#include <stdlib.h>
int main()
{
int n;
unsigned long long k = 1;
double sum = 0;
printf("请输入n的值(非负整数):");
//判断输入n的值是否符合非负整数的要求
if (scanf("%d", &n) != 1 || n < 0) {
printf("输入无效,请输入非负整数。\n");
return 1;
}
if (n == 0) {
printf("1/0!的和sum= 1.000000\n");
return 0;
}
for (int i = 1; i <= n; i++)
{
//判断n的阶乘是否超出变量范围溢出
if (k > ULLONG_MAX / i) {
printf("阶乘结果超出范围,终止计算。\n");
return 1;
}
k *= i;
sum += 1.0 / k;
printf("k = %llu, sum = %.6f\n", k, sum);
}
printf("1/n!的和sum= %.6f\n", sum);
return 0;
}
异常退出:流程控制语句
在循环结构中,循环体一般都要执行到循环条件不成立的时候才会退出循环。但是实际问题中,一些特殊的情况下,需要中途退出循环体,或者某次循环时不希望执行循环体中的某些语句,这时就需要使用到流程控制语句。
- break语句:使程序运行时中途退出switch-case结构或者退出一个循环体。
- continue语句:提前结束本次循环,跳过continue语句下面未执行的语句,继续进行下一次循环。
- 示例:
- 使用break退出循环
#include <stdio.h>
void main() {
int i, n;
for (i = 1; i <= 5; i++) {
printf("Please enter n: ");
scanf("%d", &n);
if (n < 0) break;
printf("n = %d\n", n);
}
printf("Program is over!\n");
}
// 输入:-10 输出:Please enter n: -10 Program is over!
注意:
- 在嵌套循环结构中,break语句只能退出包含break语句的那层循环体。
- break语句不能用在除了switch语句和循环语句以外的任何其他语句。
- 使用continue跳过某次循环
#include <stdio.h>
void main() {
int i, n;
for (i = 1; i <= 5; i++) {
printf("Please enter n: ");
scanf("%d", &n);
if (n < 0) continue;
printf("n = %d\n", n);
}
printf("Program is over!\n");
}
// 输入:
// 10 -10 20 -20 30
// 输出:
// Please enter n: 10 n = 10
// Please enter n: 20 n = 20
// Please enter n: 30 n = 30
// Program is over!
注意:
- continue语句通常和if语句连用,只能提前结束本次循环,不能使整个循环终止。
- continue语句只对循环起作用。
- continue语句在for语句中结束本次循环,但for语句中的增量仍然执行。
- 示例
#include <stdio.h>
int main()
{
int a = 7, i;
for (i = 1; i <= 3; i++)
{
if (a > 13)
break;
if (a % 2)
{
a += 3;
continue;
}
a = a + 4;
}
printf("%d,%d", i, a);
return 0;
}
//输出的结果是:3,14
循环编程应用
- 素数判断
- 定义:素数就是只能被1和自身整除的正整数,1不是素数,2是素数。
- 示例:
#include <stdio.h>
#include <math.h>
void main() {
int i, m, n;
scanf("%d", &m);
n = (int)sqrt(m);
for (i = 2; i <= n; i++) {
if (m % i == 0) break;
}
if (i > n) printf("%d is a prime number!\n", m);
else printf("%d is not a prime number!\n", m);
}
// 输入:17 输出:17 is a prime number!
- 百元纸币兑换
- 问题:用一张百元纸币兑换一元、五元和十元的纸币,要求兑换后纸币的总数为20张,问共有多少种换法?每种换法中各面值的纸币分别为多少张?
- 示例:
#include <stdio.h>
int main() {
int num_one, num_five, num_ten;
int total_combinations = 0;
for (num_ten = 0; num_ten <= 20/2; num_ten++) {
for (num_five = 0; num_five <= (20 - num_ten); num_five++) {
for(num_one = 0;num_one <= 20 - num_ten - num_five;num_one++){
if (100 == 10 * num_ten + 5 * num_five + num_one) {
total_combinations++;
printf("十元的张数:%d; 五元的张数:%d; 一元的张数:%d;\n", num_ten, num_five, num_one);
}
}
}
}
printf("共有%d种换法!\n", total_combinations);
return 0;
}
- 水仙花数
- 定义:所谓“水仙花数”是指一个三位数,其各位数字立方和等于该数本身,如: 153=1^3+5^3+3^3 。
- 示例:
#include <stdio.h>
void main() {
int i, j, k;
for (i = 1; i <= 9; i++) {
for (j = 0; j <= 9; j++) {
for (k = 0; k <= 9; k++) {
if (i * 100 + j * 10 + k == i * i * i + j * j * j + k * k * k) {
printf("%d\n", i * 100 + j * 10 + k);
}
}
}
}
}
// 输出:153 370 371 407
总结
- 循环结构是编程中的重要概念,通过循环可以简化重复执行的代码。
- 循环四要素:循环控制变量初始值、循环条件的设置、循环语句的编写和循环控制变量的变化。
- C语言提供了for、while、do-while三种基本的循环语句,每种语句都有其适用的场景。
- 循环嵌套可以处理更复杂的问题,但需要注意逻辑的正确性和变量的作用域。
- 流程控制语句break和continue可以用于中途退出循环或跳过某次循环,但需要谨慎使用以避免逻辑错误。