第4章 循环结构程序设计
运行环境以Dev-C++、Visual Studio 2022、MacOS的命令行和Xcode为主
0.编写循环程序要解决的3个问题
-
1.定义循环变量并赋初值
-
2.指定循环变量的范围,即循环条件
-
3.修改循环变量的值使得循环条件在某时变为假,结束循环
1.while循环:"当型"循环
- 1.1 一般形式
while (表达式) { // 表达式可以是任意类型的表达式
语句序列;
}
-
1.2 执行逻辑
-
1.计算表达式的值,若结果为真(非0),则进入2,开始一次循环周期;否则进入3
-
2.执行
{}内的语句序列(循环体) -
3.结束循环,执行while循环后方(
}之后)语句
-
-
1.3 注意
-
避免出现“死循环”,会破解死循环,例4-1前引入死循环案例,借助断点调试观察死循环的过程
-
当第一次判断表达式的值为假(0)时,循环体执行0次
-
当循环体中只有一条语句时可省略
{},但不建议这么做。养成加{}的习惯 -
一般用于循环次数不明确的情况
-
-
1.4 while死循环案例
- 1.不带变量的死循环
#include <stdio.h> #include <windows.h> int main() { while (3) { printf("2022级网络技术班!\n"); Sleep(1000); // 程序休息/睡眠1000毫秒,即1s } return 0; } // 此程序会无限次打印"2022级网络技术班!",直到按下Ctrl + C强行终止 // 造成死循环的根本原因在于"表达式"的值恒为真- 2.带变量的死循环
#include <stdio.h> int main() { int i = 3; while (i) { printf("2022级网络技术班!\n "); } return 0; } // 虽然将"表达式"由常数值换作变量,但由于变量值恒为真的本质未变,依然是死循环- 3.带变量并破解死循环代码1
// 破解死循环的关键在于:"表达式"的值在某次循环时变为0 #include <stdio.h> int main() { int i = 3; while (i) { printf("2022级网络技术班!\n "); i --; // -- i也可以 } return 0; } // 分析上述代码的执行过程和打印结果- 4.带变量并破解死循环代码2
// 为缩短代码量,可直接将循环体中的自减运算移动到"表达式"中 // 区别"i--"表达式的值和变量i本身的值,在第2章已提及 #include <stdio.h> int main() { int i = 3; while (i --) { printf("2021级卓越3班!\n"); } return 0; } // 分析上述代码的执行过程和打印结果 -
1.5 while循环案例
- 1.用while循环实现打印1 2 3 4 5
#include <stdio.h> int main(){ int i = 1; while (i <= 5) { printf("%d ", i); i++; } return 0; }- 2.编程求1+2+3+4+5之和。
// 分析:1.选择基本结构。本题一直在重复"加法"动作(累加),可用循环结构实现 // 2.如何通过变量来控制循环开始或结束? // 2.1 在程序中给定一个变量,它通常用于控制循环的开始和结束,称为"循环变量"(如案例中的"int i;"); // 2.2 循环变量通常写在"表达式"中,定义时会给定一个初值(可随意),且能确定让循环结束 // 2.3 循环变量有一定的范围(> >= < <=),循环执行过程中循环变量的值会改变(++ -- =),当其超出范围后循环结束(如while (a <= ?) a++;)。 // 3.仅仅有了循环变量还不能得到最终的结果,"循环变量" i的值遍历了1、2、3、4、5,要求它们的加和,可定义一个用于存储和的变量sum,每当i增加了1,就把i累加到sum中,即"sum = sum + i;",sum初值为0,不影响对i累加的值。当循环结束时,sum中存储的大概率就是求得的和 #include <stdio.h> int main(){ int sum = 0;// 累加器sum置0 int i = 1; // 循环变量i赋初值1 while (i <= 5) { sum = sum + i; // 将i值累加到sum中 i = i +1; // 改变循环变量i的值 } printf("1+2+3+4+5=%d", sum); return 0; }- 3.(变式)编写循环程序求1+2+3+4+5+…+100之和。
// 分析:本题思路与上题一致,只将循环范围修改即可 #include <stdio.h> int main(){ int sum = 0; int i = 1; while (i <= 100) { // 修改循环条件 sum = sum + i; i = i + 1; } printf("1+2+3+...+100=%d", sum); return 0; }- 4.(变式)编写循环程序求1-2+3-4+5-6+7-8的值。
// 方法1:该程序与上例的最大区别在于公式中有加法也有减法,需要用代码完成题目中加减号的变换,将"1-2+3-4+5-6+7-8"这个式子改头换面——从"加减交错"改为"一直加"的式子。因为累加的程序已经写过了 // 1 - 2 + 3 - 4 + 5 - 6 + 7 - 8 = 1 + (-2) + 3 + (-4) + 5 + (-6) + 7 + (-8) // 1 + 2 * (-1) + 3 + 4 * (-1) + 5 + 6 * (-1) + 7 + 8 * (-1) // 1 * 1 + 2 * (-1) + 3 * 1 + 4 * (-1) + 5 * 1 + 6 * (-1) + 7 * 1 + 8 * (-1) // 运算逻辑:1-8看作循环变量 i,每循环一次就把1的值乘1或-1后累加到sum中。 #include <stdio.h> int main() { int i = 1, sum = 0; int t = 1; while (i <= 8) { sum = sum + i * t; i ++; t = -t; } printf("%d", sum); return 0; } // 方法2:利用奇偶数,观察原式发现奇数前方的符号为"+",偶数前方的符号为"-" #include <stdio.h> int main(){ int i = 1, sum = 0; while (i <= 8){ if (i % 2 != 0) { sum = sum + i; } else { sum = sum - i; } i++; } printf("sum=%d", sum); return 0; }- 5.(变式)编程计算1 + 1/2 + 1/4 + 1/8 + 1/16 + …的值,直到最后一项值小于0.0001。
// 分析:1.此程序累加式中的规律:从首项1开始,依次乘以1/2就是下一项,但注意C程序中的1/2值为0; // 2.定义循环变量i,初值为1.0。因为循环的结束条件为某一项的值"i < 0.0001",所以循环继续的条件为其反面情况"i >= 0.0001"; // 3.定义累加变量sum,初值为0.0,每经历一次循环就将i累加到sum中,同时i的值自动减半 #include <stdio.h> int main(){ float t = 1.0; double sum = 0; while (t >= 0.0001) { sum = sum + t; t = t / 2; // 注意将t定义成浮点型,如果是整型的话t/2结果为整数,计算结果不准确 } printf(“sum=%f\n”, sum); return 0; }- 6.(变式)计算 1 - 1/3 + 1/5 - 1/7 + 1/9 - … 的和,直到某一项的绝对值小于 0.0001。
#include <stdio.h> #include <math.h> int main() { double sum = 0, term = 1; // term为累加的每一项 int d = 1, t = 1; while (fabs(term) >= 0.0001) { sum += term; d += 2; // d为分母 t = -t; term = (double)t / d; } printf("sum=%f\n", sum); return 0; }- 7.(变式)计算1 - 1/3! + 1/5! - 1/7! + ... 的和,直到某一项的绝对值小于0.0001
#include <stdio.h> #include <math.h> int fac(int n) { // 求阶乘的函数 int i = 0, ret = 1; for(i = 1; i <= n; i++) { ret = ret * i; } return ret; } int main() { int t = 1, i = 1; float term = 1.0 / i, sum = 0; while (fabs(term) >= 0.0001) { // 1.0/1 sum += term; // 0+1.0/1-1.0/3!+1.0/5! i += 2; // 3 5 t = -t; // -1 1 term = 1.0 / fac(i) * t; //1.0/3!*(-1) 1.0/5! } printf("term=%f, sum=%f", term, sum); return 0; }- 8.从键盘上输入若干个整数,直到输入0为止,求它们的乘积
// 分析:无法确定此程序的循环次数,因此选用while或do…while循环; // 累积变量的初值应设定为1。由于累乘求得的结果数值可能较大,可选择long型。 // 由于输入的数用于累乘计算,且题目明确要求直到输入0时停止计算,由此推断循环变量和用于累积计算用到的变量是同一个; // 定义累积变量mult = 1,循环变量i,初值通过scanf读取。while循环中表达式为"i != 0",累积计算表达式为"mul = mul * i" #include <stdio.h> int main( ){ int n; long t = 1; scanf("%d", &n); while (n != 0){ t= t * n; scanf("%d", &n ); } printf("%ld \n", t ); return 0; }- 9.从键盘输入4个数,找出最大数
#include <stdio.h> int main(int argc, const char * argv[]) { int i = 0 n = 0, max = 0; scanf("%d", &max); while (i < 4) { scanf("%d", &n); if (n > max) { max = n; } i++; } printf("max=%d\n", max); return 0; }
2.do...while循环:"直到型"循环
- 2.1 一般形式
do {
语句序列;
}while (表达式); // 表达式可以是任意类型的表达式
-
2.2 执行逻辑
-
1.执行循环体中的语句序列,开始一个循环周期
-
2.计算表达式的值,若结果为真(非0),则转到1,继续下一个循环周期;否则进入3
-
3.结束循环,执行
do...while循环后方的语句
-
-
2.3 注意
-
先执行循环体,后判断条件,循环次数不低于1次
-
```while (表达式)
后面的分号;不能遗漏,{}```可以省略,但不建议省略 -
一般用于循环次数不明确和循环体至少被执行一次的情形
-
-
2.4 循环案例
- 1.输入若干字符,以
*作为输入结束符,分别用do...while和while语句实现。输入相同字符,比较它们运行结果有何不同。
//分析:1.循环变量赋初值(char ch = 0;); //2.设定循环条件(ch != ‘*’); //3.循环变量值的调整(重新输入后循环体内也要加入一条scanf或 getchar输入语句) #include <stdio.h> int main() { char ch = 0; ch = getchar(); do { putchar(ch); ch=getchar(); } while (ch != '*'); return 0; } #include <stdio.h> int main() { char ch = 0; ch = getchar(); while(ch != '*') { putchar(ch); ch = getchar(); } return 0; }- 2.打印读入的字符
#include <stdio.h> int main(int argc, const char * argv[]) { int ch = 0; while ((ch = getchar()) != EOF) { // 输入a回车,'a'和'\n'都会被读入 putchar(ch); } printf("Hello, World!\n"); return 0; } // getchar()——>输入缓冲区<——键盘-
3.密码输入异常代码示例
scanf()通过输入缓冲区读取数据,键盘上输入了abcdef\n,scanf()读取了abcdefgetchar()直接从输入缓冲区中拿走了'\n'赋值给了ret,因此程序异常
![]()
// 解决方法1:在scanf()下方加一条getchar()以读取'\n' #include <stdio.h> int main(int argc, const char * argv[]) { // insert code here... char password[20] = {0}; printf("请输入密码:>"); scanf("%s", password); getchar(); // 读取了\n printf("请确认密码(Y/N):>"); int ret = getchar(); if ('Y' == ret) { printf("Yes\n"); } else { printf("No\n"); } return 0; } // 以上代码若输入"abcd ef\n",依然会出现异常,因为scanf()读到空格就会停止 // 解决方法2 #include <stdio.h> int main(int argc, const char * argv[]) { // insert code here... char password[20] = {0}; printf("请输入密码:>"); scanf("%s", password); int ch = 0; while ((ch = getchar()) != '\n') { // 清空缓冲区中的其他字符 ; } printf("请确认密码(Y/N):>"); int ret = getchar(); if ('Y' == ret) { printf("Yes\n"); } else { printf("No\n"); } return 0; }- 4.输出数字字符
#include <stdio.h> int main() { char ch = 0; while ((ch = getchar()) != EOF) { if (ch < '0' || ch > '9') { continue; } putchar(ch); } return 0; }- 5.输入一个整数,按相反次序输出。例如,输入54321,输出12345
// 分析:对于输入的任意一个整数N,可以做以下操作: // 1.判断N是不是0,如果是,结束程序/循环;如果不是,进入第2步; // 2.通过运算操作取出整数N的最后一位并打印输出,同时去掉N的最后一位得到新的整数N; // 3.返回到第1步继续运算 #include <stdio.h> int main() { int num, b; printf("请输入num的值:\n"); scanf("%d", &num); do { b = num % 10; printf("%d", b); num = num / 10; } while (num); return 0; } - 1.输入若干字符,以
3.for循环
- 3.1 一般形式
for(表达式1;表达式2;表达式3) {
语句序列;
}
-
3.2 执行逻辑
- 1.计算表达式1
- 2.计算表达式2,若结果为真,则转到3,否则进入5
- 3.执行语句序列
- 4.计算表达式3,转到2
- 5 结束循环,执行
for语句后面(}之后)的语句
![]()
-
3.3 注意
-
关于3个表达式
-
表达式1用于循环变量定义和初始化,只在循环开始时执行1次,简单表达式或逗号表达式均可。C99语法支持
for(int i = 0;...),但不建议这么写,有的编译器编译不通过 -
表达式2作为循环条件,在每一轮循环开始前计算,且建议写开区间,如
i < 10而非i <= 9 -
表达式3用于循环变量更新,简单表达式或逗号表达式均可
-
不建议在循环体中更新循环变量的值,只通过表达式3修改,以防循环失去控制
-
-
关于程序易读性
- 不建议删除
{},尤其在嵌套的循环中 - 不建议3个表达式中写逗号表达式,如下
for(sum=0, i=1; i<=10; sum=sum+i, i++) { ; }- 不建议省略3个表达式中其中1~3个,表达式2省略意味着判断会恒成立(死循环),如下
i = 1; for(; i<=10;) { sum = sum + i; i ++; } for( ; ;) { printf("hehe\n"); // 死循环 } - 不建议删除
-
关于累加和累乘变量
-
累加 sum = 0
-
累乘 multy = 1
-
-
先判断条件,后执行循环,循环次数不低于0次
-
当语句序列只有一个
;时表示空语句 -
一般用于循环次数明确的情况
-
-
3.4 案例分析
- 1.从键盘上输入10个整数,求其和。
// 分析:1.在使用while循环做过累加的编程题后,基本已经掌握了循环内部变量的累加问题。此时通常涉及到2个变量,一个用来存储累加的和,如sum;另一个作为变量累加到sum中,如i // 2.该题的关键在于:写对for循环中3个表达式。已知"表达式1"用来实现循环变量的初始化工作,"表达式2"作为循环条件,"表达式3"用于循环变量的更新。 // 表达式1:i = 0 表达式2:i <= 9 / i < 10 表达式3:i ++ for(i = 0; i <= 9; i ++) { xxx } // 注意:由于本题中循环变量的值仅仅用于控制循环次数,并未和用于计算的10个数据的值发生关联,所以i的初值可以随意定义,只要能保证循环10次后结束就可以 #include <stdio.h> int main( ){ int i = 0, num = 0; int sum = 0; for(i = 1; i <= 10; i++){ scanf("%d", &num); sum = sum + num; } printf("这10个整数的和为:%d", sum ); return 0; }- 2.输入n的值,计算n的阶乘
#include <stdio.h> int main() { int i = 1, n = 0; int ret = 1; scanf("%d", &n); for (i = 1; i <= n; i++) { ret = ret * i; } printf("%d\n", ret); return 0; }- 3.输入n的值,计算1! + 2! + 3! + 4! + ... + n!
#include <stdio.h> // 方法1:重复计算比较多,执行效率低 int main(int argc, const char * argv[]) { // insert code here... int i = 0, j = 0; int sum = 0, ret = 0; for (i = 1; i <= 10; i++) { ret = 1; // 每次开始计算i的阶乘时先将累积值重置为1 for (j = 1; j <= i; j++) { ret = ret * j; } printf("%d的阶乘为%d\n", i, ret); sum = sum + ret; } printf("sum=%d\n", sum); return 0; } #include <stdio.h> // 方法2:减少了重复运算,执行效率高 int main(int argc, const char * argv[]) { // insert code here... int n = 0, ret = 1; int sum = 0; for (n = 1; n <= 10; n++) { ret = ret * n; sum = sum + ret; } printf("sum=%d\n", sum); return 0; }
4.多重循环
- 4.1 一般形式
for(表达式1;表达式2;表达式3) {
for(表达式4;表达式5;表达式6) {
语句序列;
}
}
while(表达式) {
while(表达式) {
语句序列;
}
}
-
4.2 执行逻辑(以双重for循环为例)
-
1.执行表达式1进行循环变量outer的初始化,随后执行表达式2
-
1.1 若结果为真,则进入内循环,继续按照for循环的执行逻辑运行
-
1.2 若结果为假,则退出外循环,循环结束
-
-
2.当内循环结束退出后,执行表达式3,随后执行表达式2,继续1.1或1.2
-
-
4.3 注意事项
-
1.循环嵌套层数不宜过多,2~3层即可。嵌套过多时考虑是否是算法逻辑有问题
-
2.不要漏掉
{},否则会导致程序可读性差,分析某代码属于哪一层循环 -
3.理清代码执行逻辑,确认语句是在程序哪一部分起作用(外循环外、外循环内&&内循环外、内循环内等)
-
4.外循环的循环变量outer初值、内循环循环变量inner初值、内外循环的取值范围(表达式2和表达式5)是无关联的还是相互限制的
-
-
4.4 嵌套循环案例
- 1.打印九九乘法表
// 分析:1.目前写过的循环程序:while、do...while、for 全是"一维"的,"九九乘法表"和之前的循环程序最大的差别在于它是"二维"的; // 2.从宏观上看,每一行的乘式的被乘数都是此行的行号;从微观上看,任意一行的某几个乘式中,乘数的值严格受被乘数约束。 // 具体分析如第5行,被乘数均为5,乘数从左到右依次为 1—2—3—4—5,即被乘数 5 约束着乘数为1~5,其他行也同样,被乘数 N 约束着乘数为 1~N; // 3.假设现在刚好有一个变量 i,它控制了乘法表的行数,则i的范围介于 1~9。针对 1~9 这 9 个数,任意抽出一个都可以代表某一行,则该行的算式个数 j 一定满足 j <= i 。 // 4. i 和 j 刚好构成了双层for循环的外侧和内侧。如何区分内层和外层循环?被控制的一方即为内层循环。 // 5.乘法表中存在一个"换行"动作,当每一行的乘数中所有可能的取值被遍历结束后,重新开始下一行。反映到程序上就是当j = i时换行,即内循环结束时换行 #include <stdio.h> int main( ) { int i = 0, j = 0; for (i = 1; i < 10; i++) { printf ( "%8d", i ); } printf (" \n---------------------------------" ); printf ("-----------------------------------\n" ); for (i = 1; i < 10; i++) { for (j = 1; j <= i; j++) { printf ("%2d*%d=%-3d ", i, j , i*j ); } printf ("\n"); } return 0; }- 2.分别输入5名学生的4门科目成绩,计算出每名学生的平均成绩
// 分析:1.这个题目也是"二维"的,一方面涉及到学生,另一方面涉及到和每一位学生绑定的成绩,即成绩由学生和科目共同决定; // 2.从"学生"来看,总共有5名,可以用 i 从 1~5 遍历; // 3.从"科目"来看,总共有4门,可以用 j 从 1~4 遍历; // 4.九九乘法表中乘数的取值范围受被乘数控制,因此将被乘数作为外层循环,将乘数作为内层循环!类似地,本题假设科目受学生控制,将学生作为外循环,科目作为内循环; // 5.计算平均成绩avg_score要先计算总成绩sum,先由程序读入成绩score; #include <stdio.h> int main(){ int stu_num = 0, course_num = 0; float score = 0.0f, avg_score = 0.0f; for (stu_num = 1; stu_num < 6; stu_num++){ float sum = 0.0f; for(course_num = 1; course_num < 5; course_num++){ scanf("%f", &score); sum = sum + score; } avg_score = sum / 4; printf("%d号学生的平均成绩是: %f\n", stu_num, avg_score); } return 0; } // 本题在学过第5章后也可用数组的方式求解
5.break语句和continue语句
-
5.1 执行逻辑
-
break用于循环结构可使程序提前跳出循环(强行结束),也可用于跳出switch结构 -
continue使程序跳过循环体中后方语句,结束本轮循环,直接进入下一轮循环
-
-
5.2 适用结构
-
break多用于for、while、do...while、switch语句,可配合if使用 -
continue多用于for、while、do...while,可配合if使用
-
-
5.3 注意
-
break跳出的是它所在的那一层循环,可能是内循环,也可能是外循环 -
break跳出的是它所在的那一层switch,在嵌套的switch语句中尤其要注意 -
可使用调试程序演示
break和continue的数据流向
-
-
5.4 案例分析
- 1.求不同半径的圆柱体的体积,只计算体积在100以下的圆柱体(体积计算公式:V = π * R^2 * H)。
// 分析:1.规定半径的范围,从小到大依次遍历,可以使用for循环; // 2.为方便程序的运行,可以指定一个固定的高度; // 3.计算每一组 (R, H) 确定的圆柱体积,一旦体积高于100,立刻通过break语句跳出循环 #include <stdio.h> #define PI 3.14 int main( ) { int r = 0; float h = 0.0f; double v = 0.0; printf("请输入圆柱体的高:" ); scanf("%f", &h ); for(r = 1; r <= 10; r++){ v = PI * r * r * h; if (v > 100.0) { break; } printf("半径等于%d, 体积等于%.2f\n", r, v); } printf("此时r = %d\n", r); return 0; }- 2.输出50~100之间所有不能被7整除的整数(计数变量的思想)。
// 分析:1.依次遍历50-100,可使用for循环,用循环变量i; // 2.循环体中判断所遍历的数中能否被7整除,能的话就直接开始下一个遍历的数字/结束本轮循环,否则原样输出; // 3.为优化显示结果,可以每行显示10个数,用到计数变量count #include <stdio.h> // 方法1:此程序用continue关键字实现 int main() { int num = 0, cnt=0; for (num = 50; num <= 100; num++){ if (num %7 == 0) { continue; } printf("%5d", num); cnt++; if(cnt%10==0){ printf("\n"); } } printf("\n"); return 0; } #include <stdio.h> // 方法2:此程序不用关键字continue int main(){ int num = 0, count = 0; for (num = 50; num <= 100; num++){ if (num%7 != 0) { printf("%d ", num); count++; if(count % 10 == 0){ printf("\n"); } } } return 0; }- 3.程序调试案例
#include <stdio.h> int main() { int i = 1; while(i <= 10) { i++; if(5 == i) { continue; // break; } printf("%d ", i); i++; } return 0; }
6.循环结构程序设计实例
-
1.判断一个数是否是素数
![]()
// -------------------------------方法1-------------------------------
// 分析:判断素数的本质还是在于找到一个非1非本身的因数,只要存在这个因数,它就不是素数。
// 因此对于任意一个自然数 N(N非 0 非 1),我们要逐一确定 2 ~ N - 1 之内的所有数中是否存在被 N 整除的,只要存在一个就说明 N 不是素数,否则是素数。
// 用for循环遍历 2 ~ N - 1,一旦出现非 0 非 1 的因数n就立刻结束循环,后续 n + 1 ~ N - 1 的遍历都没有必要进行了。 i 充当循环变量,num充当用于判断的数。
// for循环有2种退出方式,要么“寿终正寝”,要么“强行break”。前者一定是因为触发了"表达式2"不满足的件,即循环变量i的值一直增加到了num,此时num一定是素数!
#include <stdio.h>
int main() {
int i = 0, num = 0;
printf("请输入num的值:\n");
scanf("%d", &num);
for (i = 2; i <= num - 1; i ++) {
if (num % i == 0) {
break;
}
}
if (num == i) {
printf("%d是素数!\n", num);
}
else {
printf("%d不是素数!\n", num);
}
return 0;
}
// -------------------------------方法2-------------------------------
// 分析:有必要逐一确定 2 ~ N - 1 之内的所有因数吗?
// 如何把参考的因数范围缩小?可以通过范例去总结,如36,按照以往思路需要逐一验证 2 ~ 35,但对于 32、33、34、35 这一类数没必要去验证,它们非常接近36且基本不可能被36整除。
// 虽然本来循环也没执行到35,到2时就强行break了。但我们总会遇到执行到底的数字,比如17,按照以前的算法它会判断到16。对于 N,需要从 2 ~ N - 1 之间找出一个界限L,
// 只判断 2 ~ L 以内的因数就可以了。如何去找这个界限L呢?对于36,开平方6是它的一个因数,除以2得到18也是它的一个因数,它们均可作为界限L (2, 18) (3, 12) (4, 9) (6, 6) (9, 4) (12, 3) (18, 2)。
// 再如 27,开平方后介于 5 ~ 6 之间的这个数可以作为界限。只要 2 ~ 界限L 内的所有整数不是因数,那它就一定是素数!
// 用for循环遍历 2~ 界限L,一旦出现非0非1的因数n就立刻结束循环,后续 n + 1 ~ 界限L 的遍历都没有必要进行了。
#include <stdio.h>
#include <math.h>
int main() {
int i, num;
printf("请输入num的值:\n");
scanf("%d", &num);
for(i = 2; i <= sqrt(num); i ++) {
if(num % i == 0) {
break;
}
}
if(i > sqrt(num)) {
printf("%d是素数!\n", num);
}
else {
printf("%d不是素数!\n", num);
}
return 0;
}
// -------------------------------方法3-------------------------------
// 再用一种"标志变量"的方式打印,"标志变量"在编程中是一种很重要的思想。
// 它的初值设为 1(即默认 num 是素数),一旦 for 循环以 break 形式结束,此时 num 一定不是素数,就在if语句块中将"标志变量"置为0。
// 循环结束后通过判断"标志变量"的值就可以断定num是不是素数了。
#include <stdio.h>
#include <math.h>
int main() {
int i = 0, num = 0;
int flag = 1;
printf("请输入num的值:\n");
scanf("%d", &num);
for (i = 2; i <= sqrt(num); i++) {
if (num % i == 0) {
flag = 0;
break;
}
}
if (flag == 1) {
printf("%d是素数!\n", num);
}
else {
printf("%d不是素数!\n", num);
}
return 0;
}
// 思考:1.代码中的"if(flag == 1)"能否改为"if(flag = 1)"
// 2.代码中的"if(flag == 1)"能否改为"if(flag)"
- 2.找出100~200之间的所有素数
// 分析:在详细分析了判断num是否是素数的算法后,本题又增加了一个限制条件,要求找到100-200内的素数。
// 不同处在于:之前是输入一个数num,判断是否是素数,现在是100-200逐个判断,即外层再套一个for循环遍历一下100-200。
// 关键:tag标志变量写在哪个位置更合适?
// tag标志变量要放在外循环内,内循环外,因为内层循环是对因数的判断,在判断开始前我们先默认最外层循环的这个数是素数,然后
// 根据内层循环的执行情况再决定是否对其重新赋值为0!简而言之,在每一轮最外层循环开始时(即判断一个新的数是否是素数),都要重新对flag标志变量赋值。
#include <stdio.h>
#include <math.h>
int main() {
int num = 0, j = 0;
int tag = 0, m = 0, cnt = 0;
for (num = 101; num < 200; num += 2) { // num增量为2
tag = 1;
m = sqrt(num);
for (j = 2; j <= m; j++) {
if(num % j == 0) {
tag=0;
break;
}
}
if (tag == 1) {
printf("%5d", num);
cnt++;
if(cnt % 12 == 0) {
printf("\n");
}
}
}
return 0;
}
- 3.编写一个函数prime,用于输出10-1000以内的个位数与十位数之和为9的素数
#include <stdio.h>
#include <math.h>
int prime(int num) {
int i = 0, tag = 1;
int m = sqrt(num);
for (i = 2; i <= m; i++) {
if (num % i == 0) {
tag = 0;
break;
}
}
if(tag && (num % 10 + num / 10 % 10 == 9)) {
return 1;
}
return 0;
}
int main() {
int i = 0;
for (i = 10; i <= 1000; i++) {
if (prime(i)) {
printf("%-4d", i);
}
}
return 0;
}
- 4.输入两个整数,求它们的最大公约数。方法是采用“碾转相除法”,即反复模除取余,直到余数为0
// 分析:自然语言法描述辗转相除法:
// 1.将第1个整数a模除第2个整数b(a>b),得到余数r;
// 2.将a赋值为b,将b赋值为r;
// 3.重复前2步,直到r为0(循环)
// 补充:假设有两个整数a、b,它们的最大公约数是p,最小公倍数是q。那么有这样的关系:ab=pq。
// -------------------------------方法1-------------------------------
#include <stdio.h> // 用while循环实现
int main() {
int a = 0, b = 0;
int temp = 0, r = 0;
printf("input a、b:\n");
scanf("%d %d", &a, &b);
if(a < b) {
temp = a;
a = b;
b = temp;
}
r = a % b;
while(r) {
a = b;
b = r;
r = a % b;
}
printf("最大公约数为: %d\n", b);
return 0;
}
// -------------------------------方法2-------------------------------
#include <stdio.h> // 用do...while循环实现
int main() {
int a = 0, b = 0;
int temp = 0, r = 0;
printf("input a、b:\n");
scanf("%d %d", &a, &b);
if(a < b) {
temp = a;
a = b;
b = temp;
}
do {
r = a%b;
a = b;
b = r;
}while(r);
printf("最大公约数为: %d\n", a);
return 0;
}
- 5.“百鸡问题”是我国古代数学家张丘建在他编写的《算经》里提出的一个不定方程问题,即“鸡翁一,值钱五,鸡母一,值钱三,鸡雏三,值钱一,百钱买百鸡。问鸡翁、母、雏各几何?”
// 分析:设公鸡(鸡翁)、母鸡(鸡母)和小鸡(鸡雏)的数量各为x、y和z只,共100钱,若全部买公鸡,则最多买20只,显然x的变化范围在0~20之间。
// 同理,y的变化范围在0~33之间,所得的不定式方程应为方程1和方程2的联立。
// x + y + z = 100 方程1
// 5x + 3y + z / 3 = 100 方程2
// 注意:不定式方程组可能有多组解,小鸡数量必须是3的倍数,也即方程2中的 z / 3 必须能够整除,即程序中需要加一个限定条件“(z % 3 == 0)”,
// 不然会得到一些错误答案(如(4, 18, 79)、(4, 18, 80)),因为 z / 3 会舍弃小数部分。
#include <stdio.h>
int main(){
int x = 0, y = 0, z = 0;
for(x = 0; x <= 20; x++) {
for(y = 0; y <= 34; y++) {
z = 100 - x - y;
if ((z % 3 == 0) && (5 * x + 3 * y + z / 3 == 100)) {
printf("公鸡:%-2d 母鸡:%-2d 小鸡:%-2d\n", x, y, z );
}
}
}
return 0;
}
- 6.(百鸡问题变式1)从3个红球(x)、5个白球(y)和6个黑球(z)中任意取出8个,且必须要有红球和白球,请输出所有方案。
// 分析:本题和“百鸡问题”的本质一样。红球、白球和黑球的个数类比上题中公鸡、母鸡和小鸡的数量,三种球共8个对应上题中共100只。
// x + y + z = 8 方程1
// x >= 1 且 y >= 1 方程2
#include <stdio.h>
int main() {
int x = 0, y = 0, z = 0;
for (x = 1; x <= 3; x++) {
for (y = 1; y <= 5; y ++) {
z = 8 - x - y;
printf("红球:%d个 白球:%d个 黑球:%d个", x, y, z);
}
}
return 0;
}
- 7.(百鸡问题变式2)有1 2 3 4共4个数,能组成多少个互不相同且无重复数字的3位数?输出所有可能。
// -------------------------------方法1-------------------------------
#include <stdio.h> // 使用嵌套循环实现
int main() {
int x = 0, y = 0, z = 0;
for (x = 1; x <= 4; x++) {
for (y = 1; y <= 4; y++) {
for (z = 1; z <= 4; y++) {
if (i != j && j != k && i != k) {
printf("%d\t", i * 100 + j * 10 + k);
}
}
printf("红球:%d个 白球:%d个 黑球:%d个", x, y, z);
}
}
return 0;
}
// -------------------------------方法2-------------------------------
#include <stdio.h> // 使用拆分三位数的各位 十位 百位来实现
int main() {
int x = 0, i= 0, j = 0, k = 0;
for (x = 102; x <= 987; x++) {
i = x % 10; // 拆分个位数
j = x/10%10; // 拆分十位数
k = x/100; // 拆分百位数
if(i != k && i != k && j != k) {
printf("%d\t", x);
}
}
return 0;
}
- 8.输入n的值,计算1+(1+2)+(1+2+3+)···+(1+2+3+···+n)的值。
#include <stdio.h>
int main(){
int sum = 0, total = 0;
int i = 0, n = 0;
scanf("%d", &n);
for(i = 1; i <= n; i++){
sum = sum + i;
total = total + sum;
}
printf("total=%d\n", total);
return 0;
}
- 9.【改编 2024网络技能考试】春考报名系统在注册用户时,用户名有以下要求:
- 1.用户名只能有大小写字母和数字,不能出现其他字符
- 2.大写字母、小写字母和数字必须都要出现
/*
方法1:不用函数
小写字母ASKII码范围97-122 a-z
大写字母ASKII码范围65-90 A-Z
数字字符的范围48-57 0-9
*/
#include <stdio.h>
#include <string.h>
int main(){
char user_name[21] = {0}, t = 0;
int i = 0;
int upper_flag = 0, lower_flag = 0, num_flag = 0;
printf("请输入用户名:\n");
gets(user_name);
int length = strlen(user_name);
if(length < 8 || length > 20) { // 写代码的准则:简单的逻辑处理放在前面,复杂的逻辑放在后面。
printf("用户名长度不合法,请保证长度在8-20之间!\n");
return 0;
}
else {
for(i = 0; user_name[i] != '\0'; i++) {
t = user_name[i];
if(t < 48 || (t > 57 && t < 65) || (t > 90 && t < 97) || (t > 122)){
printf("只能包含大小写字母和数字!\n");
return 0;
}
else {
if(num_flag == 0 && (t >= 48 && t<=57)) {
num_flag = 1;
}
else if(upper_flag == 0 && (t >= 65 && t <= 90)) {
upper_flag = 1;
}
else if(lower_flag == 0 && (t >= 97 && t <= 122)) {
lower_flag = 1;
}
}
}
if(num_flag == 1 && lower_flag == 1 && upper_flag == 1) {
printf("合法用户名!");
}
}
return 0;
}
// 方法2:使用函数(带返回值,退出函数)
#include <stdio.h>
#include <string.h>
int check_name(char name[]){
char t = 0;
int length = strlen(name), i = 0;
int num_flag = 0, upper_flag = 0, lower_flag = 0;
if(length < 8 || length > 20) {
return -1;
}
else {
for(i = 0; name[i] != '\0'; i++) {
t = name[i];
if(t < 48 || (t > 57 && t < 65) || (t > 90 && t < 97) || (t > 122)) {
return -2;
}
else {
if(num_flag == 0 && (t >= 48 && t<=57)) {
num_flag = 1;
}
else if(upper_flag == 0 && (t >= 65 && t <= 90)) {
upper_flag = 1;
}
else if(lower_flag == 0 && (t >= 97 && t <= 122)) {
lower_flag = 1;
}
}
}
if(num_flag == 1 && lower_flag == 1 && upper_flag == 1) {
return 0;
}
}
}
int main(){
char user_name[21] = {0};
int flag = 0;
printf("请输入用户名:\n");
gets(user_name);
flag = check_name(user_name);
if(flag == 0){
printf("合法用户名!");
}
else if(flag == -1) {
printf("用户名长度不合法,请保证字符串长在8-20之间!\n");
}
else {
printf("只能包含大小写字母和数字!\n");
}
return 0;
}
// 方法3:使用函数(不带返回值,退出程序)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void check_name(char name[]){
char t = 0;
int length = strlen(name), i = 0;
int num_flag = 0, upper_flag = 0, lower_flag = 0;
if(length < 8 || length > 20) {
printf("用户名长度不合法,请保证字符串长在8-20之间!\n");
exit(-1);
}
else {
for(i = 0; name[i] != '\0'; i ++) {
t = name[i];
if(t < 48 || (t > 57 && t < 65) || (t > 90 && t < 97) || (t > 122)) {
printf("只能包含大小写字母和数字!\n");
exit(-2);
}
else {
if(num_flag == 0 && (t >= 48 && t<=57)) {
num_flag = 1;
}
else if(upper_flag == 0 && (t >= 65 && t <= 90)) {
upper_flag = 1;
}
else if(lower_flag == 0 && (t >= 97 && t <= 122)) {
lower_flag = 1;
}
}
}
if(num_flag == 1 && lower_flag == 1 && upper_flag == 1) {
printf("合法用户名!");
exit(0);
}
}
}
int main(){
char user_name[21] = {0};
int flag = 0;
printf("请输入用户名:\n");
gets(user_name);
check_name(user_name);
return 0;
}
- 10.输入一串字符串,判断是字母字符串、数字字符串还是其他字符串
#include <stdio.h>
#define MAX 100
int main(){
char s[MAX] = {0}, t = 0;
int i = 0, alpha_flag = 0, num_flag = 0;
printf("请输入一串字符串:\n");
gets(s);
while(s[i] != '\0'){
t = s[i];
if(alpha_flag == 0&&((t>='A'&&t<='Z')||(t>='a'&&t<='z'))){
alpha_flag = 1;
}
else if(num_flag == 0&&(t>='0'&&t<='9')){
num_flag = 1;
}
i++;
}
if(alpha_flag==1&&num_flag==0){
printf("是字母字符串");
}
else if(alpha_flag==0&&num_flag==1){
printf("是数字符串");
}
else{
printf("是其他符串");
}
return 0;
}




浙公网安备 33010602011771号