第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...whilewhile语句实现。输入相同字符,比较它们运行结果有何不同。
    //分析: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\nscanf()读取了abcdef
      • getchar()直接从输入缓冲区中拿走了'\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;
    }
    
    

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语句中尤其要注意

    • 可使用调试程序演示breakcontinue的数据流向

  • 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;
}
posted @ 2024-12-30 09:20  pycoder_666  阅读(264)  评论(0)    收藏  举报