【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 @   陈景中  阅读(208)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示