循环结构
什么是循环
代码的重复执行,就叫做循环。
循环的分类
-
无限循环:程序设计中尽量避免无限循环。(程序中的无限循环必须可控)
-
有限循环:循环限定循环次数或者循环的条件。
循环的构成
-
循环体
-
循环条件
当型循环的实现
while
语法:
while(循环条件) { 循环语句; }
说明:
-
循环条件的返回值必须是布尔类型,在C语言中,布尔类型为真使用 非0 来表示,布尔类型为假使用 0 表示。
-
{} 包起来的内容整体称之为为 循环体 。
-
我们要在 循环体 中控制 循环条件 的变化,否则会产生死循环。
执行过程:
特点:
先判断,后执行,循环体语句有可能一次都不执行。
案例:
/* 需求:while循环案例:如何编程来计算1 + 2 + 3 + … + 100? */ #include <stdio.h> int main() { // 定义一个变量,保存累加和 int sum = 0; // 定义一个循环变量 int i = 1; while(i <= 100) // 循环条件,这里可以有关系表达式、逻辑表达式,表达式运算结果是boolean(1,0) { // 累加计算 // sum = sum + i; sum += i; // 给循环变量赋值,用来影响循环条件 i++; } printf("1~100的累加和是:%d\n",sum); return 0; }
案例:
/* 需求:while循环案例-计算1~100之间的偶数和,也就是2+4+6+8+..+100的累加和 */ #include <stdio.h> int main() { // 创建一个变量,用来存放累加和 int sum = 0; // 创建循环变量,初始值2 int i = 2; while(i <= 100) { // 排除奇数 if( i % 2 == 0) // 表示能被2整除,说名这个数是偶数 { sum += i; } i++;// 改变循环变量的值,此时一定要注意这句代码的位置 } printf("1~100之间的偶数的累加和是:%d\n",sum); return 0; }
死循环
while(1) // 我们会在死循环进行必要的限制
for..
语法:
for(①表达式1;②表达式2;③表达式3) { ④循环体语句; }
说明:
-
() 中可以只保留两个分号,举例: (;;)
-
① 是循环变量,我们需要赋初值,循环变量可以是列表,多个循环变量使用逗号分隔,举例: int i=0,j=0
-
② 是循环条件,用来限制循环的次数,循环条件支持关系表达式,如果加入逻辑表达式,会变成复合表达式,举例: i < 10 && j < 10
-
③ 改变循环条件,支持列表,这里可以使用赋值表达式,举例: i++,j++
-
执行顺序:①②④③ --> ②④③ --> ②④③ ... --> ②,这里①只执行1次。
执行过程:
特点:
先判断,后执行,循环体语句有可能一次都不执行。
案例:
/** * 需求:for循环案例-求1~100之间的偶数和 */ #include <stdio.h> // while实现 void while_test() { int sum = 0; int i = 1; while(i <= 100) { if(i % 2 == 0) { sum += i; } i++; } printf("while实现:1~100之间的偶数和:%d\n",sum); } // for实现 void for_test() { int sum = 0; for(int i = 1;i <= 100;i++) // 死循环表示:while(1)、for(;;) { if(i % 2 == 0) { sum += i; } } printf("for实现:1~100之间的偶数和:%d\n",sum); } int main() { while_test(); for_test(); return 0; }
案例:
/* 需求:for案例-用户可通过键盘录入一个整数n,用来作为阶乘的最高位数 */ #include <stdio.h> #include <math.h> int main() { int n = 1;// 接受控制台输入,作为阶乘最高位 int r = 1;// 用来接收计算结果 printf("请输入一个整数:\n"); scanf("%d",&n); // 循环实现阶乘 for(int i = 1;i <= fabs(n);i++) { r *= i; } printf("1~%d之间的阶乘的结果是:%d\n",n,r); return 0; }
总结
-
for语句使用语法规则上,降低/避免因为忘记循环条件更新操作,而引起的产生无限循环的几率。
-
应用场合:for语句往往应用于循环次数事先可以确定的场景。
死循环
for(表达式1;;表达式3); for(;;);
循环实现的三要素
-
循环变量初始化
-
循环条件
-
循环变量更新
案例:
/* 需求:for案例-求Fibonacci数列前20个数。 */ #include <stdio.h> int main() { int f1 = 1;// 前一个数,默认是1,因为第1个是1 int f2 = 1;// 后一个数,默认是1,因为第2个是1 int i = 1;// 循环变量 for(;i <= 20;i++) { printf("%12d%12d",f1,f2); // 一行显示两个数,每两个换一行 if(i % 2 == 0) { printf("\n"); } f1 = f1 + f2; // f1 = 2 = 1+1 f2 = f2 + f1; // f2 = 3 = 1+2 } return 0; }
直到型循环的实现
do..while
语法:
do { 循环体; } while(循环条件);
说明:
-
循环条件的返回值必须是布尔类型,在C语言中,布尔类型为真使用 非0 来表示,布尔类型为假使用 0 表示。
-
{} 包起来的内容整体称之为为 循环体 。
-
我们要在 循环体 中控制 循环条件 的变化,否则会产生死循环。
执行过程:
特点:
先执行,后判断,循环体语句至少执行一次。
案例:
/* 需求:do..while案例:求1~100之间奇数的累加和 */ #include <stdio.h> int main() { // 创建一个变量,用来存储累加和 int sum = 0; // 创建循环变量,默认值1 int i = 1; do { // 判断i是否是奇数 if(i % 2 != 0) { sum += i; } i++;// 改变循环变量的值 }while(i <= 100); printf("1~100之间奇数的累加和是:%d\n",sum); return 0; }
循环的嵌套
3种循环(while、do……while、for)可以互相嵌套。在前一个循环结构的内部又存在一个完整的循环
结构,如:
案例:
/** * 需求:嵌套for循环案例-求100~200之间的所有素数 */ #include <stdio.h> int main() { int num = 100;// 存放100~200之间的自然数 int i = 2;// 循环变量,默认从2开始,因为自然数除以1没有意义 102,100~102, 102%100,102%101 int isPrime = 1;// 用来记录1~自身之间能整除的次数 6, 2~5, 6%2,6%3,6%4,6%5 // 第1层for循环:生成100~200之间的自然数 for(;num <= 200; num++) { // 默认是素数 isPrime = 1; // 第2层循环,生成 2~自身-1的自然数,用于和自身校验,是否能够整除,如果有1个能被整除,就说明不是素数 for(i = 2; i < num -1; i++) { if(num % i == 0) { isPrime = 0;// 不是素数 } } if(isPrime) // 条件判断:isPrime == 1 缩写 isPrime { printf("%d ",num); } } printf("\n"); return 0; }
循环结构的典型应用场景
-
求累和:举例
1+2+3+4+..+100的和
-
求累乘:举例
1*2*3*4*..*100的积
-
求均值:举例: (1+2+3+4+..+100) / 100的值
-
求极值:举例: 12,34,55,2,66中的最大值或者最小值
-
元素遍历:常用于数组元素的遍历,比如:从 [1,2,3,4,5] 获取每一个元素。数组我们后续课程讲解。
-
...
基础算法模型
-
累加和
-
定义一个变量(sum),并赋初值为0;
-
用该变量累加(+=)每一个数据项(i)
-
当访问完每一个数据项,此时该变量的取值就是累加和的结果。
-
累乘
-
定义一个变量,并赋初值为1;
-
用该变量累乘(*=)每一个数据项;
-
当访问完每一个数据项,此时该变量的取值就是累乘的结果。
-
极值【知识点:数组】
-
定义一个变量,并赋初值为第一个数据项
-
从第二个数据项开始,一次性与该变量进行比较,如果大于/小于该变量,则将当前数据项的数据赋值给该变量。
-
当访问完每一个数据项,此时该变量的取值就是求极值的结果。
break和continue
-
break
功能:
-
用在switch中,用来跳出switch的case语句;如果case没有break,可能会产生case穿透。
-
用在循环中(while、do..while、for..),提前结束循环,也就是跳出整个循环。
说明:
-
break不能用于循环语句和switch语句之外的任何其它语句之中。
-
break只能终止并跳出最近一层的结构。
图示:
-
-
案例:
/** * 需求:break案例-马杰参与一阶段分段考试,如果考试及格,就跳出循环,否则就一直考 */ #include <stdio.h> int main() { printf("一阶段分段考试\n"); // 声明一个变量,用来存放考试成绩 int score = 0; do { // int score = 78; // score的作用域只能作用域到{}以内 printf("小马哥开始一阶段分段考试...\n"); scanf("%d",&score); // 开始合格,结束考试 if(score >= 60) { break; } } while(1); printf("恭喜小马哥考试通过!\n"); return 0; }
案例:
/** * 需求:break案例-输出1~100以内的偶数,只输出前10个 */ #include <stdio.h> int main() { for(int i = 1,count = 1;i <= 100;i++) { if(i % 2 == 0) { if(count > 10){ break; } count++;// 注意位置:只记录偶数 printf("%d\t",i); } } printf("\n"); return 0; }
-
continue
功能:continue语句不会结束整个循环,而是跳过本次循环尚未执行的语句,进入下一次循环。
说明:
-
仅用于循环语句中。
-
在嵌套循环的情况下,continue语句只对包含它的最内层的循环体语句起作用。
图示:
-
-
案例:
/** * 需求:continue案例-求1~100之内的偶数和 */ #include <stdio.h> // 不使用continue void fun01() { int sum = 0; for(int i = 1; i <= 100; i++) { // 判断偶数 if(i % 2 == 0) { sum += i; } } printf("1~100之内的偶数和是:%d\n",sum); } // 使用continue void fun02() { int sum = 0; for(int i = 1; i <= 100; i++) { // 判断奇数 if(i % 2 != 0) { continue;// 跳出所有的奇数 } sum += i; } printf("1~100之内的偶数和是:%d\n",sum); } int main() { fun01(); fun02(); }
-
跳出多层循环
跳出多层循环是不能使用break和continue的,因为他们只能跳出单层循环,跳出多层循环,需要
我们自己定义标志位进行跳出。
案例:
// 定义一个标志位 int isFlag = 1;// 默认循环成立 char fu; while(isFlag) { printf("第一层循环执行的内容...\n"); while(isFlag) { printf("第二层循环执行的内容...\n"); printf("是否跳出循环?(y/n)"); scanf("%c",&fu); if(fu == 'Y' || fu == 'y') { isFlag = 0; } } }
注意:如果是多层循环,进的时候是从外到内,跳出的时候是从内到外。
综合案例:猜拳游戏
案例代码:
/* 需求:猜拳游戏 步骤: 1. 选择对手 2. 自己出拳 3. 对手出拳 4. 双方比较 */ #include <stdio.h> #include <string.h> #include <time.h> #include <stdlib.h> int main() { // 管理变量 int isStop = 1;// 退出状态,默认是连续 char stop;// 退出游戏 int computer_no; // 玩家编号 char computer_name[6];// 玩家姓名 c语言不支持 char[6] computer_name 样的写法 int is_select_ct = 1;// 默认,选择对手操作是不重复的 int is_select_own = 1;// 默认,自己出拳操作是不重复的 int own_no; // 自己出拳的序号 char own_name[6];// 自己出拳的名称 int player_no;// 对手出拳的序号 char player_name[6];// 对手出拳的名称 // 游戏头部 printf("*******************************************\n"); printf("************ 猜拳游戏 v1.0版 **************\n"); printf("*******************************************\n"); while(isStop) { // 选择对手 while(is_select_ct) { printf("请选择您的对战玩家:\n[1].李白 [2].唐三 [3].萧炎\n"); scanf("%d",&computer_no); switch(computer_no) { case 1: // c语言中,对char数组直接赋值 strcpy(computer_name,"李白"); is_select_ct = 0; break; case 2: strcpy(computer_name,"唐三"); is_select_ct = 0; break; case 3: strcpy(computer_name,"萧炎"); is_select_ct = 0; break; default: strcpy(computer_name,"无效玩家"); is_select_ct = 1; } printf("您选择的对手是:%s\n",computer_name); } // 自己出拳 while(is_select_own) { printf("\n请选择您出拳方式:\n[1].石头 [2].剪刀 [3].布\n"); scanf("%d",&own_no); switch(own_no) { case 1: // c语言中,对char数组直接赋值 strcpy(own_name,"石头"); is_select_own = 0; break; case 2: strcpy(own_name,"剪刀"); is_select_own = 0; break; case 3: strcpy(own_name,"布"); is_select_own = 0; break; default: strcpy(own_name,"无效的出拳"); is_select_own = 1; } printf("您的出拳方式是:%s\n",own_name); } // 对手出拳(随机生成0~2) srand((unsigned)time(NULL));// 以时间作为随机种子 // 随机生成对手出拳的序号(0~2) player_no = rand()%3+1; switch(player_no) { case 1: // c语言中,对char数组直接赋值 strcpy(player_name,"石头"); break; case 2: strcpy(player_name,"剪刀"); break; case 3: strcpy(player_name,"布"); break; } printf("\n%s的出拳方式是:%s\n",computer_name,player_name); // 双方比较 if((own_no == 1 && player_no == 2)||(own_no == 2 && player_no == 3)||(own_no == 3 &&player_no == 1)) { printf("\n恭喜,您获胜!\n"); } else if(own_no == player_no) { printf("\n平局!\n"); } else { printf("\n很遗憾,您输了!\n"); } // 退出游戏 printf("\n是否退出游戏?[Y/N]\n"); getchar();// 消除回车 scanf("%c",&stop); if(stop == 'Y' || stop == 'y') { is_select_ct = 0;// 默认,选择对手操作是不重复的 is_select_own = 0;// 默认,自己出拳操作是不重复的 isStop = 0; printf("\n游戏结束!\n"); } else { // 还原状态 is_select_ct = 1;// 默认,选择对手操作是不重复的 is_select_own = 1;// 默认,自己出拳操作是不重复的 isStop = 1; } } return 0; }
补充
-
什么是素数
素数也被称作质数,只能被1和自身整除的数就叫做素数。