循环结构
对应教学日历:讲次 8-10(4月9日-4月16日)
一、while 循环
while (表达式) // 表达式为真时循环继续
{
循环体;
}- 先判断,后执行:如果一开始条件就为假,循环体一次也不执行
- 循环体内必须有使条件趋向于假的语句,否则是死循环
典型模式:未知循环次数
while (scanf("%d", &n) != EOF) { ... } // 读到文件末尾
while ((ch = getchar()) != '\n') { ... } // 读入一行
while (n != 0) { n = n / 10; count++; } // 统计位数二、do-while 循环
do
{
循环体;
} while (表达式); // 注意:这里有分号!- 先执行,后判断:循环体至少执行一次
while后面有分号,不要漏掉
典型应用:至少执行一次的场景
do {
printf("请输入正整数:");
scanf("%d", &n);
} while (n <= 0); // 用户输入非法时重新输入三、for 循环
for (表达式1; 表达式2; 表达式3)
{
循环体;
}
// 表达式1:初始化(只执行一次)
// 表达式2:条件判断(每次循环前判断)
// 表达式3:循环变量更新(每次循环后执行)执行流程
① 初始化 → ② 判断条件(假则退出)→ ③ 执行循环体 → ④ 更新变量 → 回到②
for 与 while 的等价关系
for (i=0; i<n; i++) ≈ i=0;
{ while (i<n)
循环体; {
} 循环体;
i++;
}for 的特殊用法
for (;;) { ... } // 无限循环(省略三个表达式)
for (i=0, j=0; i<10; i++, j+=2) { ... } // 逗号表达式
for (; i<100; ) { ... i++; } // 省略初始化和更新(等价于 while)四、循环的嵌套
for (i = 0; i < n; i++) // 外循环
for (j = 0; j < m; j++) // 内循环
{
// 内层每执行完一轮,外层才执行一次
}循环嵌套可以用于处理二维问题:九九乘法表、矩阵遍历、图形打印。
五、辅助控制语句
break
- 跳出当前最近的一层循环或 switch
- 常用于提前终止循环
for (i=0; i<100; i++)
{
if (a[i] == target)
break; // 找到了,跳出循环
}continue
- 跳过本次循环的剩余语句,进入下一次循环迭代
- 不跳出循环,只结束当前迭代
for (i=0; i<100; i++)
{
if (a[i] < 0) continue; // 跳过负数
sum += a[i];
}break vs continue
| break | continue | |
|---|---|---|
| 作用范围 | 循环 + switch | 仅循环 |
| 效果 | 跳出循环 | 跳过本次剩余,继续下一次 |
六、循环经典应用
1. 穷举法(枚举法)
在有限范围内逐一检查所有可能。
// 百钱买百鸡:公鸡5元,母鸡3元,小鸡1元3只,100元买100只
for (g=0; g<=20; g++)
for (m=0; m<=33; m++)
{
x = 100 - g - m;
if (5*g + 3*m + x/3.0 == 100)
printf("公鸡%d 母鸡%d 小鸡%d\n", g, m, x);
}2. 迭代法(递推法)
用前一步结果推导下一步。
// 辗转相除法求最大公约数
while (n != 0)
{
t = m % n;
m = n;
n = t;
}
// m 即为最大公约数3. 累加累乘
sum = 0; // 累加初始化为 0
for (i=1; i<=100; i++)
sum += i;
product = 1; // 累乘初始化为 1
for (i=1; i<=n; i++)
product *= i; // n!4. 有规律图形打印
// 打印金字塔
for (i=1; i<=n; i++)
{
for (j=1; j<=n-i; j++) printf(" "); // 空格
for (j=1; j<=2*i-1; j++) printf("*"); // 星号
printf("\n");
}作业示例
ex7-2:while 循环——骑车还是走路
#include <stdio.h>
int main()
{
int n;
double dist;
scanf("%d", &n);
while (n--) { // n 次循环
scanf("%lf", &dist);
double bikeTime = 50.0 + dist / 3.0;
double walkTime = dist / 1.2;
if (bikeTime < walkTime) {
printf("Bike\n");
} else if (walkTime < bikeTime) {
printf("Walk\n");
} else {
printf("All\n");
}
}
return 0;
}要点:
while (n--)循环 n 次(n 递减到 0 停止)%lf用于double的 scanf
ex7-4:while 循环——大小写转换
#include<stdio.h>
int main ()
{
char ch;
printf("请输入字符:\n");
while((ch=getchar())!='#')
{
if(ch>='A'&&ch<='Z')
ch=ch+32;
else if(ch>='a'&&ch<='z')
ch=ch-32;
putchar(ch);
}
}要点:
(ch=getchar())!='#'→ 赋值表达式ch=getchar()的值是读到的字符- 循环直到输入
#结束 - 大小写转换:±32
ex7-6:while——统计正负数与平均值
#include<stdio.h>
int main ()
{
int input,p,n;
float average,sum=0;
p=n=0;
scanf("%d",&input);
while(input!=0)
{
if(input>0) p++;
if(input<0) n++;
sum=sum+input;
scanf("%d",&input); // 循环体内继续读入
}
if(p+n==0)
printf("No point.");
else
{
average=sum/(p+n);
printf("%d\n%d\n%.f\n%.2f",p,n,sum,average);
}
return 0;
}要点:
while(input!=0)0 作为输入结束标志- 循环体内
scanf读取下一个数,否则死循环 - 注意除零判断:
p+n==0
ex8-1:do-while 猜数游戏(含次数限制)
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
int n, rand_num, guess;
int count = 0;
scanf("%d", &n);
srand((unsigned int)time(NULL));
rand_num = rand() % 100 + 1;
while (1) {
scanf("%d", &guess);
if (guess < 0) { // 输入负数退出
printf("Game Over\n");
break;
}
count++;
if (count > n) { // 超次数
printf("Game Over\n");
break;
}
if (guess > rand_num) {
printf("Too big\n");
} else if (guess < rand_num) {
printf("Too small\n");
} else {
if (count == 1) printf("Bingo!\n");
else if (count <= 3) printf("Lucky You!\n");
else printf("Good Guess!\n");
break;
}
}
return 0;
}要点:
- 用
while(1)+break实现灵活退出 - 多层判断:负数退出、超次数退出、猜对退出
break跳出当前最近 while
ex8-2:辗转相除法求最大公约数
#include <stdio.h>
int main() {
int m, n, t, a, b;
scanf("%d %d", &m, &n);
a = m; b = n;
if (m < n) { // 确保 m >= n
t = m; m = n; n = t;
}
while (n != 0) { // 辗转相除核心
t = m % n;
m = n;
n = t;
}
printf("最大公约数为:%d\n", m);
printf("最小公倍数为:%d\n", a * b / m);
return 0;
}要点:
- 辗转相除法:重复用
m % n直到余数为 0,最后的除数即最大公约数 - 最小公倍数 = 两数之积 / 最大公约数
ex8-3:嵌套 for——三角九九乘法表
#include <stdio.h>
int main()
{
for (int i = 1; i <= 9; i++) {
for (int j = 1; j <= 9; j++) {
if (j < i) {
printf(" "); // 空白填充
} else {
printf("%4d",i*j);
}
}
printf("\n");
}
return 0;
}要点:
- 双重循环:外层控制行,内层控制列
%4d使每个输出占 4 格宽度,对齐整齐
ex8-4:完数查找(嵌套 for)
#include <stdio.h>
int main()
{
int i, j, sum;
for (i = 1; i <= 1000; i++) {
sum = 0;
for (j = 1; j < i; j++) {
if (i % j == 0) {
sum = sum + j; // 累加真因子
}
}
if (sum == i) {
printf("%d\n", i); // 完数:因子和等于自身
}
}
return 0;
}要点:
- 完数定义:所有真因子之和等于本身的数(如 6 = 1+2+3,28 = 1+2+4+7+14)
sum每次外循环必须重置为 0- 内循环查找 i 的所有因子
ex8-5:素数判断与打印(嵌套 for + break)
#include <stdio.h>
int main() {
int count = 0;
for (int num = 3; num <= 300; num++) {
int isPrime = 1;
for (int i = 2; i * i <= num; i++) { // 只需检查到 sqrt(num)
if (num % i == 0) {
isPrime = 0;
break;
}
}
if (isPrime) {
printf("%4d ", num);
count++;
if (count % 10 == 0) printf("\n"); // 每行 10 个
}
}
printf("\n");
return 0;
}要点:
- 素数判断优化:只需检查到
i * i <= num(等价于i <= sqrt(num)) break一旦发现因子立即跳出count % 10 == 0控制每 10 个换行
作业重点
- while:先判断后执行,可能一次都不执行
- do-while:先执行后判断,至少执行一次(注意末尾分号)
- for:最常用的已知次数循环,
for(初始化; 条件; 更新) - 循环嵌套:外层每执行一次,内层执行所有轮次;总执行次数 = 外层次数 × 内层次数
- break 跳出当前循环;continue 跳过本次剩余语句
- 经典算法必须掌握:辗转相除法、素数判断、完数查找、九九乘法表
- 循环体内要有使条件趋近于假的语句,否则死循环
- 累加变量初始化为 0,累乘变量初始化为 1