【C语言】MOOC翁恺老师-入门-分支结构与循环结构-3

三、分支结构与循环结构

1. 判断


  • 判断语法

    // if 
    if(条件){
        ....
    }
    
    // if后没有{},仅作紧跟的一句处理后结束
    if(条件)
        ....
    
    // if-else
    if(条件){
        ....
    }else{
        ....
    }
    
    // if-elif-else
    if(条件){
        ....
    }else if(条件){	/* Python是elif */
        ....
    }else{
        ....
    }
    
  • C语言中判断后true == 1;flase == 0

  • 问题:

    1. /* */是注释,编译器不管其中的内容。但是有没有想过编译器会把它替换成什么呢?是一个空格?还是不留痕迹?

      答: 在把代码翻译成汇编代码前,还有一个步骤叫预处理。 预处理之后注释代码会被处理掉。 一条语句前的注释 => 等长的空格; 一条语句中的注释 => 一个空格; 一条语句后的注释 => 直接删除。



2. 循环


  • 循环语法

    // while,其它情况用while
    while(循环条件){
        循环体..
    }
    
    
    // do-while, 至少执行循环体一次。如果必须执行一次,用do-while
    do {
        循环体..
    }while(循环条件);
    
    
    // for,如果知道固定次数用for
    for(初始条件; 循环条件; 步进式){	//括号中每一个表达式都可以省略
        循环体..
    }
    

  • while练习-计算某个数的位数

    # include <stdio.h>
    
    int main()
    {
    	int x;
    	int n = 0;
    	
    	scanf("%d", &x);
    	
    	n++;			
    	x /= 10;		// 从右往左依次划掉一位
    	while(x > 0){		
    		n++;
    		x /= 10;
    	}
    	
    	printf("%d", n);
    	return 0;
     } 
    

  • for循环的循环次数思考

    • for( i = 0; i<n; i++)
      
    • 循环的次数是n,而循环结束以后,i的值是n。循环的控制变量i,是选择从0开始还是从1开始是判断i<n还是判断i<=n,对循环的次数,循环结束后变量的值都有影响。如下所示

      // 1.
      for(i=0; i<5; i++){
        	...
      }
      i=0; i=1; i=2; i=3; i=4, 最后i=5,共5次
          
      // 2.
      for(i=1; i<=5; i++){
          ...
      }
      i=1; i=2; i=3; i=4; i=5, 最后i=6,共5次
      
    • 任何for循环都能改写成while循环

      image


  • 验证测试的方法

    1. 测试用例一定要多种情况都考虑到,正数,负数,0...
    2. 断点
    3. 人脑模拟电脑处理枚举情况,排除逻辑上的错误
    4. 在代码中穿插打印语句,看到每次循环的结果
    5. 可以在代码中加上打印语句,提醒自己现在运行到哪个位置

  • 问题

    1. for循环是高级语言出现的第一种循环,它和现代的程序设计理念是否存在差距?为什么Python语言就没有C语言这种形式的for循环了?

      答:因为for循环不符合人类的语言逻辑,并不易于理解,因此就造成了第一次接触语言的人难以理解for循环所表达的意思。而现代程序设计理念更强调与人类语言的逻辑统一性而非编译逻辑。故在新兴的python语言中祛除掉了C中的for循环逻辑,改用了python独有的for循环形式in range。但由于for循环抽象程度高,所以在掌握语言基础后,其表现逻辑更为简洁易于回溯,反而更方便使用。



3. 进一步的判断与循环


3.1 逻辑类型和运算

  • 逻辑运算

    • C99标准bool类型:引入#include <stdbool.h>后才能使用bool和true,false

      # include <stdbool.h>
      
      int main()
      {
          bool b = 6>5;
          bool t = true;	
          /* 虽然bool类型可以用true赋值,但是后续t=2也可以通过编译。另外C并没有规定输出时候什么格式输出bool, 实际上输出后它还是1-true, 0-false */
          t = 2;	
          printf("%d\n", b);
          return 0;
      }
      
  • 逻辑运算符:

    • && 、|| 是短路,左边能决定了就不会再做右边判断了,要小心点。
    • 编码习惯:不要把赋值,包括复合赋值放进判断表达式中,例如a>1 && a++<2
  • 条件运算符

    • 三目运算符,用于条件判断,例如count = (count > 20) ? count - 10 : count +10;

3.2 级联和嵌套的判断

  • else的匹配:如果没有大括号的情况下,else总和最近的 if 匹配。为了避免代码阅读时出错,我们始终在 if 或 else后面用 { },即使只有一条语句的时候也如此,养成良好的编码习惯。

3.3 多路分支

  • switch语法

    switch( 控制表达式 ){	// 控制表达式只能是整数型
        case 常量:	// 常量可以是常数,也可以是常数计算的表达式
            ...;
            break;
        case 常量:
            ...;
            break;
        default:
            ....;
    }
    

3.4 循环的例子

  • 套路:

    • 计算之前先保存原始的值,后面可能有用
  • 练习1:猜数的游戏

    # include <stdio.h>
    # include <stdlib.h>	// rand()产生随机数的库函数头文件
    # include <time.h>
    
    int main()
    {
        
        int guess;
        int count;
        srand(time(0));
        int number = rand()%100+1;	// %100是产生100以内的数
        						  // x%n的结果是产生【0, n-1】的一个整数
        printf("please insert your guess_number between 1 to 100.\n");
        scanf("%d", &guess);
        while(guess != number){
            if(guess < number){
                printf("your guess_number is too small.\n");
            }else{
                printf("your guess_number is too big.\n");
            }
            count++;
            scanf("%d", &guess);
        }
        printf("wonderful!! you costed %d times to guess it right.", count);
        return 0;
    }
    
    • 猜数游戏,为什么100以内的数猜7次就够了?

      答:二分法,2的7次方是2的次方里面第一个比100大的

  • 练习2:数字的逆序

    # include <stdio.h>
    
    int main()
    {
        int x;
        scanf("%d", &x);
        
        int digit;
        int ret = 0;
        
        while( x>0 ){
            digit = x % 10;		// 取最后一位
            ret = ret*10 + digit;	// 进行逆序处理
            x /= 10;	// 删掉最后一位
        }
        printf("%d", ret);
        return 0;
    }
    


4. 循环控制


  • 嵌套循环练习:从2开始打印前50个素数

    // 素数:大于1的自然数除了1和它自身整除
    # include <stdio.h>
    
    int main()
    {
        int count = 0;	// 前50个数的计数
        int x;			// 用户输入当前的数字x,进行往后判断
        scanf("%d", &x);
        
        while( count<50 ){
            int isPrime = 1;	// 设置判断flag
            for( int j = 2; j<x; j++ ){		// j从2开始
                if( x % j == 0){
                    isPrime = 0;
                    break;
                }
            }
            if( isPrime == 1 ){
                printf("%d\n", x);
                count++;
            }
            x++;
        }
        printf("sum_count:%d times", count);
        return 0;
        
    }
    
  • 多重循环练习:凑硬币(如何用1角,2角和5角的硬币凑出10元以下的金额?)

    # include <stdio.h>
    
    int main()
    {
        int x;
        int one, two, five;
        int exit = 0;	// 设置限制,使得到一个方案后执行接力break跳出
        
        scanf("%d", &x);
        for( one = 1; one < x*10; one++){
            for( two = 1; two < x*10/2; two++){
                for( five = 1; five < x*10/5; five++){
                    if( one + two*2 + five*5 == x*10 ){
                        printf("可以用%d个1角+%d个2角+%d个5角得到%d元\n", one, two, five, x);
                        exit = 1;	// 表示获得一个方案,执行接力break
                        break;
                    }
                }
                if(exit == 1) break;
            }
            if(exit == 1) break;    
        }
        return 0;
    }
    
    • 接力break是一种跳出多重循环的判断,还有另一种起到同样效果的就是goto语句。goto语句适合于多重循环跳出外层循环的场景
    # include <stdio.h>
    
    int main()
    {
        int x;
        int one, two, five;
        
        scanf("%d", &x);
        for( one = 1; one < x*10; one++){
            for( two = 1; two < x*10/2; two++){
                for( five = 1; five < x*10/5; five++){
                    if( one + two*2 + five*5 == x*10 ){
                        printf("可以用%d个1角+%d个2角+%d个5角得到%d元\n", one, two, five, x);
    				  goto out;		// 入口
                    }
                }
            }
        }
    out: // 出口
        return 0;
    }
    

    建议:goto用多了程序会变得复杂,但在某些情况下使用更为方便。比如:从多重循环中直接跳出 ,减少了接力break。


4.1 循环应用

  • 练习1:求和:f(n) = 1+ 1/2 + 1/3 + 1/4 + .... + 1/n

    # include <stdio.h>
    
    int main()
    {
        double sum = 0.0;
        int n;
        scanf("%d", &n);
        
        for(int i=1; i<=n; i++){	// 起点和终点都确定,用for循环
            double j = 1.0/i;
            sum += j;
        }
        printf("f(%d)=%.2lf\n", n, sum);
        return 0;
    }
    
  • 练习2:求和:f(n) = 1 - 1/2 + 1/3 - 1/4 + .... + 1/n

    # include <stdio.h>
    
    int main()
    {
        double sum = 0.0;
        int n;
        scanf("%d", &n);
        
        int flag = 1;	// 符号标志
        for(int i=1; i<=n; i++){
    		sum += flag*1.0/i;
             flag = -flag;
        }
    	printf("f(%d)=%.2lf\n", n, sum);
        return 0;
    }
    
  • 练习3:求两个数的最大公约数(普通方法)

    # include <stdio.h>
    
    int main()
    {
        int a, b;
        int min;
    
        scanf("%d %d", &a, &b);
        
        int ret = 0;
        int i;
        for(i=1; i < a<b?a:b; i++){
            if(a % i == 0 && b % i == 0){
                    ret = i;
            }
        }
        printf("%d和%d的最大公约数是%d.\n", a, b, ret);
        return 0;
    }
    
    • 求两个数的最大公约数(辗转相除法)

      /*
      1. 如果b等于0, 计算结束, a就是最大公约数;
      2. 否则,计算a除以b的余数,让a等于b,而b等于那个余数;
      3. 回到第一步
      for example:
      a   b2   t
      12  18  12
      18  12  6
      12  6   0
      6   0——> exit——>ret = 6
      */
      
      # include <stdio.h>
      int main()
      {
          int a, b, t;
          scanf("%d %d", &a, &b);
          int ret = a;
          int b_two = b;
          while(b_two!=0){
              t = ret % b_two;
              ret = b_two;
              b_two = t;
          }
          printf("%d和%d的最大公约数是%d.\n", a, b, ret);
          return 0;
      }
      
  • 练习4:正序分解整数(数组方法)

    # include <stdio.h>
    
    int main()
    {
        int number;
        int count = 0;
        scanf("%d", &number);
        int t = number;
        while(t > 0){	// 判断数是几位数,得出数组应该得有几位数据
            t /= 10;
            count++;
        }
        
        int numbers[count-1];	// 定义几个数据的数组类型0~count-1
        int i = 0;
        while(number > 0){
            int single = number % 10;
            numbers[i] = single;
    		number /= 10;
            i++;
        }
        for(int j=count-1; j>=0; j--){
    		if(j==0 || j!=count-1)	// 间隔空格条件
    			printf(" ");
            printf("%d", numbers[j]);
        }
        return 0;
    }
    
    • 正序分解整数(普通方法)

      /*
      x = 13425;
      13425 / 10000 ——> 1
      13425 % 10000 ——> 3425
      10000 / 10 ——> 1000
      
      3425 / 1000 ——> 3
      3425 % 1000 ——> 425
      1000 / 10 ——> 100
      
      425 / 100 ——> 4
      425 % 100 ——> 25
      100 / 10 ——> 10
      
      25 / 10 ——> 2
      25 % 10 ——> 5
      10 / 10 ——> 1
      
      5 / 1 ——> 5
      5 % 1 ——> 5
      1 / 10 ——> 0
      */
      # include <stdio.h>
      
      int main()
      {
          int x;
          scanf("%d", &x);
          
          int mask = 1;
          int t = x;
          while(t > 9){		// 判断x是几位数,让mask变成相对应位数
              t /= 10;
              mask *= 10;
          }
          printf("x = %d, mask = %d", x, mask);
          
          do{				// 将x进行拆分
              int d = x / mask;
              printf("%d", d);
              if( mask > 9){	// 判断如果不是最后一位即有空格输出
                  printf(" ");
              }
              x %= mask;
              mask /= 10;
          }while( mask > 0 );
          return 0;
      }
      
posted @ 2022-05-08 15:25  陈景中  阅读(141)  评论(0编辑  收藏  举报