作者: Wenbo

  • 第 6 讲:让计算机做复杂的事情——循环应用

    一、循环结构回顾

    1. 循环四要素

    • 循环体:循环中重复执行的核心操作。
    • 循环初始条件:循环控制变量的初始化。
    • 循环控制表达式:构建循环控制条件。
    • 循环控制变量的修改:构建循环退出机制。

    2. 三种循环语句

    • for循环for(E1; E2; E3) 语句S;
    • while循环while(E2) { 语句S; E3; }
    • do...while循环do { 语句S; E3; } while(E2);

    3. 循环结构与选择结构的区别

    • 循环结构:用于重复执行某段代码。
    • 选择结构:用于根据条件选择执行不同的代码。

    4. 多重循环的执行过程

    • 外层循环每执行一次,内层循环从头执行一遍。
    • 循环嵌套时,内层循环是外层循环体中的一条语句。

    二、灵活退出循环:breakcontinue

    1. break语句

    • 功能:强制中断循环,立即退出当前循环。

    • 语法

      while(表达式1) {
          语句1;
          if(表达式2) break;
      }
      
    • 注意事项

      • 只能用于循环语句和switch语句中。
      • 在多重循环中,break只能跳出一层循环

    2. continue语句

    • 功能:跳过本次循环中剩余语句,直接进入下一次循环。

    • 语法

      while(表达式1) {
          语句1;
          if(表达式2) continue;
          语句2;
      }
      
    • 区别

      • break:终止整个循环。
      • continue:终止本次循环,继续下一次循环。

    三、枚举法(穷举法)

    1. 概念

    • 枚举法是指对问题域中所有可能的解进行穷举搜索,并根据条件筛选出符合要求的解。

    2. 基本模式

    while (在搜索空间内) {
        if (满足判决条件) {
            输出解;
        }
        更新可能的解;
    }
    

    3. 示例:韩信点兵问题

    • 问题描述:士兵人数满足以下条件:
      1. x % 5 == 1
      2. x % 6 == 5
      3. x % 7 == 4
      4. x % 11 == 10
    • 解法:从1开始逐一枚举,直到找到满足所有条件的数。

    四、枚举法的优化策略

    1. 启发式搜索

    • 利用问题信息引导搜索,减少搜索范围。
    • 示例:韩信点兵问题中,利用x % 5 == 1,可令x每次增加5,而不是1。
    • 优化效果
      • 原始搜索:10000次循环。
      • 优化1(增加5):2111次循环。
      • 优化2(增加11):423次循环。
      • 优化3(增加55):192次循环。

    2. 优化思路

    • 根据约束条件,调整搜索步长,减少无效枚举。
    • 示例:结合多个约束条件,选择最大公约数或最小公倍数作为步长。

    五、综合应用:百钱百鸡问题

    1. 问题描述

    • 鸡翁一值钱五,鸡母一值钱三,鸡雏三值钱一。
    • 百钱买百鸡,问鸡翁、鸡母、鸡雏各几何?

    2. 解法思路

    • 使用三重循环枚举鸡翁、鸡母、鸡雏的数量。
    • 约束条件:
      • 5 * cock + 3 * hen + chick / 3 == 100
      • cock + hen + chick == 100
      • chick % 3 == 0
    • 优化:根据约束减少循环次数。

    六、总结

    1. 循环应用要点

    • 熟练使用breakcontinue控制循环流程。
    • 掌握枚举法的基本思想与实现方式。
    • 学会通过优化搜索策略提高程序效率。

    2. 核心思想

    • 循环 + 条件判断是解决枚举问题的基本模式。
    • 启发式优化是提高枚举效率的关键。
    • 编程不仅是实现功能,更是精益求精的过程。

    七、思考题

    回顾用数学方法和循环求解“物不知数”、“韩信点兵”等问题的过程,思考还有哪些问题适合用循环求解?如何结合数学知识优化枚举过程?

  • 实验 5:让计算机做复杂的事情——循环结构

    一、实验目标

    1. 能够运用计算思维分析解决简单的实际问题。
    2. 能够熟练应用 whiledo...whilefor 语句的基本语法和执行流程。
    3. 能够在 VS2010 开发环境中综合运用循环结构编写较强功能的程序。

    二、知识回顾

    1. 循环的四要素

    • 循环体:重复执行的核心操作。
    • 循环初始条件:初始化循环控制变量。
    • 循环控制条件:构建循环控制表达式。
    • 循环控制变量的修改:构建循环退出机制,避免死循环。

    2. 三种循环语句

    (1)while 循环

    • 语法

      while (表达式) {
          循环体;
      }
      
    • 特点:先判断后执行,循环体执行次数为 0 到多次。

    (2)do...while 循环

    • 语法

      do {
          循环体;
      } while (表达式);
      
    • 特点:先执行后判断,循环体至少执行一次。

    (3)for 循环

    • 语法

      for (表达式1; 表达式2; 表达式3) {
          循环体;
      }
      
      • 表达式1:初始化循环变量。
      • 表达式2:循环控制条件。
      • 表达式3:更新循环变量。
    • 特点:结构紧凑,适合已知循环次数的情况。

    3. 循环注意事项

    • 循环控制变量必须在循环开始前初始化。
    • 循环体内必须有修改控制变量的语句,确保循环能正常终止。
    • 避免死循环:确保循环条件最终能变为假。
    • 若进入循环前条件为假,则循环体一次也不执行(whilefor)。

    三、实验内容与实现

    1. 实验1:统计字符串中各类字符个数

    • 要求:输入一行字符,统计英文字母、空格、数字和其他字符的个数。
    • 实现方法
      • 使用 while((c = getchar()) != '\n') 逐字符读取。
      • 使用 if-else 结构判断字符类型并计数。
    • 扩展:可分别统计大写字母、小写字母、数字、空格等。

    2. 实验2:输出所有“水仙花数”

    • 定义:一个3位数,其各位数字的立方和等于该数本身,如 (153 = 1^3 + 5^3 + 3^3)。
    • 实现方法
      • 方法一:遍历100到999的所有数,分解出各位数字,判断是否满足条件。
      • 方法二:使用三重循环遍历百位、十位、个位,组合成三位数进行判断。
    • 输出:153、370、371、407。

    3. 实验3:猴子吃桃问题

    • 问题描述:猴子每天吃前一天剩下的一半又一个,第10天只剩1个,求第一天摘了多少桃子。
    • 解决方法:逆向推导,从第10天开始往前推:
      • 第10天:1个
      • 第9天:(1 + 1) × 2 = 4个
      • 通用公式:(前一天 = (当天 + 1) × 2)
    • 扩展:若每天吃前一天剩下的一半又两个,结果会不同。

    4. 实验4:牛顿迭代法求方程的根

    • 方法:利用切线逼近方程的根。
      • 迭代公式:(x_{n+1} = x_n – \frac{f(x_n)}{f’(x_n)})
      • 终止条件:(|x_{n+1} – x_n| < \varepsilon)
    • 示例方程:(2x^3 – 4x^2 + 3x – 6 = 0)
    • 实现:使用 do...while 循环进行迭代,直到满足精度要求。
    • 扩展:可修改初始值、输出迭代次数和每次迭代结果,分析不同初始值对迭代收敛的影响。

    四、重点与难点

    重点

    • 循环结构的构建。
    • 三种循环语句的语法与使用。
    • 循环与选择结构的结合运用。

    难点

    • 实际问题中循环要素的判定与设计。
    • 循环嵌套的逻辑理解与实现。
    • 迭代算法的设计与实现(如牛顿迭代法)。

    五、思考题

    1. 如何灵活中断循环的执行?举例说明(如使用 breakcontinue)。
    2. 多重循环语句的执行过程是怎样的?内外层循环如何协同工作?
  • 第 5 讲:让计算机做复杂的事情——循环结构

    一、循环结构概述

    1. 为什么需要循环结构

    • 用于处理规律性的重复计算或操作
    • 避免代码冗余,提高代码简洁性和功能性。
    • 例如:输入1000名学生的身高并计算平均身高。

    2. 循环结构的四要素

    1. 循环体:重复执行的核心操作。
    2. 循环初始条件:循环控制变量的初始化。
    3. 循环控制条件:决定是否继续循环的表达式。
    4. 循环控制变量的修改:使循环能正常退出,避免死循环。

    二、三种循环语句

    1. while 循环

    • 语法形式

      while (表达式) {
          循环体;
      }
      
    • 执行流程

      1. 判断表达式是否为真(非0)。
      2. 若为真,执行循环体,返回步骤1;否则退出循环。
    • 特点

      • 先判断后执行。
      • 循环体执行次数:0到多次
      • 循环体可以是空语句、表达式语句、函数调用、控制语句或复合语句。
    • 注意事项

      • 必须有修改循环控制变量的语句。
      • 避免死循环。

    2. do-while 循环

    • 语法形式

      do {
          循环体;
      } while (表达式);
      
    • 执行流程

      1. 先执行一次循环体。
      2. 判断表达式是否为真。
      3. 若为真,继续执行;否则退出。
    • 特点

      • 先执行后判断。
      • 循环体至少执行1次
    • 适用场景:适用于初始条件不易确定,且至少需执行一次的情况。

    3. for 循环

    • 语法形式

      for (表达式1; 表达式2; 表达式3) {
          循环体;
      }
      
    • 执行流程

      1. 执行表达式1(初始化)。
      2. 判断表达式2(控制条件)。
      3. 若为真,执行循环体,再执行表达式3(更新),返回步骤2;否则退出。
    • 特点

      • 结构紧凑,适合已知循环次数的情况。
      • 循环体执行次数:0到多次
    • 灵活用法

      • 可以省略表达式1、表达式2或表达式3(但分号不能省略)。
      • 表达式1和表达式3可以是逗号表达式。
      • 循环体可为空语句,操作可移至表达式3中。

    三、循环嵌套

    1. 概念

    • 一个循环体内包含另一个循环,称为循环嵌套
    • 外层循环每执行一次,内层循环从头执行一遍。
    • 常见有二重循环、三重循环等。

    2. 嵌套类型

    • 控制变量无交叉:内层循环次数固定。
    • 控制变量有交叉:内层循环次数依赖于外层循环变量。

    3. 示例:打印图形

    • 打印5×5星号矩阵:

      for (i = 0; i < 5; i++) {
          for (j = 0; j < 5; j++) {
              printf("* ");
          }
          printf("\n");
      }
      
    • 打印右三角形星号:

      for (i = 1; i <= 5; i++) {
          for (j = 1; j <= i; j++) {
              printf("*");
          }
          printf("\n");
      }
      

    四、循环应用实例

    1. 累加求和(1到1/100)

    • 使用 whiledo-whilefor 实现。

    • 核心代码:

      sum = 0;
      for (n = 1; n <= 100; n++) {
          sum += 1.0 / n;
      }
      

    2. 求最大值与最小值

    • 思路:将第一个输入的数作为初始最大值和最小值,后续输入逐一比较更新。

    • 代码示例:

      max = min = d;  // d为第一个输入的数
      for (i = 2; i <= 10; i++) {
          scanf("%d", &d);
          if (d > max) max = d;
          if (d < min) min = d;
      }
      

    3. 计算平均分

    • 循环输入多个成绩并累加,最后计算平均值。

    五、重点与难点

    重点

    • 循环结构的构建方法。
    • whiledo-whilefor 语句的使用。
    • 循环嵌套的程序设计。

    难点

    • 循环条件的正确设置与更新,避免死循环。
    • 嵌套循环的逻辑理解与编写。
    • 循环与选择结构的结合使用。

    六、思考题

    1. 选择结构与循环结构的区别是什么?
    2. whiledo-while 如何等价转换?
  • 实验 4:让计算机做复杂的事情——选择结构

    一、实验目标

    1. 能够正确使用 if 语句实现两分支、多分支选择结构。
    2. 能够正确使用 switch 语句实现多分支选择结构。
    3. 初步形成编写C程序解决复杂问题的能力。

    二、知识回顾

    1. if 语句实现两分支选择结构

    • 语法形式:

      if (表达式) 
          语句1;
      else 
          语句2;
      
    • 表达式可以是任意合法的C表达式。

    • 语句1和语句2可以是单条语句或复合语句(用 {} 括起来的多条语句)。

    • 执行逻辑:若表达式为“真”(非0),执行语句1;否则执行语句2。

    2. if 语句嵌套实现多分支选择结构

    • 语法形式:

      if (表达式1) 
          语句1;
      else if (表达式2) 
          语句2;
      else 
          语句3;
      
    • 注意事项

      • else 总是与它上面最近的未配对的 if 配对。
      • 可使用 {} 明确配对关系,增强代码可读性。
      • 建议缩进对齐,便于理解嵌套结构。

    3. switch 语句实现多分支选择结构

    • 语法形式:

      switch (表达式) {
          case 常量1:
              语句1;
              break;
          case 常量2:
              语句2;
              break;
          // ...
          default:
              语句n;
      }
      
    • 注意事项

      • 表达式必须是整型或字符型。
      • case 后面的常量值必须互不相同,且与表达式类型一致。
      • 若无 break,程序会继续执行下一个 case,直到遇到 breakswitch 结束。
      • default 分支可选,用于处理未匹配的情况。

    三、实验内容与要点

    1. 实验1:比较三个数大小(输出最小值)

    • 使用两两比较法,先假设第一个数为最小,再依次与后两个数比较。

    • 代码示例(整型):

      if (min > b) min = b;
      if (min > c) min = c;
      

    2. 实验2:计算小于等于1000的正数的平方根

    • 使用 sqrt() 函数(需包含 <math.h>)。

    • 注意输入数据合法性判断:

      if (x <= 1000) {
          // 计算平方根
      } else {
          // 提示输入过大
      }
      

    3. 实验3:分段函数计算

    • 使用多分支 if-else if-else 结构。

    • 注意条件范围的划分,如:

      if (x < 1) y = x;
      else if (x < 10) y = 2*x - 1;
      else y = 3*x - 11;
      

    4. 实验4:百分制成绩转等级

    • 方法一:使用 if-else if-else 结构。

    • 方法二:使用 switch 结构,结合 score/10 进行等级匹配。

    • 增强程序健壮性:添加输入合法性检查(0~100之间):

      if (score > 100 || score < 0) {
          printf("成绩输入错误\n");
      }
      

    5. 实验5(思考题):三个数从小到大排序

    • 方法一:最值法(找出最大值、最小值、中间值)。

    • 方法二:排序法(两两比较并交换):

      if (a > b) { temp = a; a = b; b = temp; }
      if (a > c) { temp = a; a = c; c = temp; }
      if (b > c) { temp = b; b = c; c = temp; }
      

    四、重难点总结

    重点

    • 选择结构的构建方法。
    • if 语句及其嵌套的使用。
    • switch 语句的使用。

    难点

    • if 嵌套的配对逻辑。
    • switch 语句中 break 的作用与使用时机。
    • 如何确保程序能覆盖所有可能的输入情况,增强程序的完备性和健壮性。

    五、思考

    1. 如何优化三个数的排序算法?
    2. 是否能用更简洁的代码实现?
  • 第 4 讲:让计算机做复杂的事情 – 选择结构

    一、核心概念:选择结构的作用

    选择结构是程序三大基本结构(顺序、选择、循环)之一,用于实现“根据条件执行不同操作”的逻辑,让计算机具备“判断决策”能力。

    二、二分支选择结构(二选一)

    二分支结构根据条件真假,从两个操作中选择一个执行,核心实现方式为if语句和条件运算符。

    1. if语句实现二分支

    (1)基本格式

    if (表达式) 语句1;
    else 语句2;
    

    (2)执行逻辑

    • 计算“表达式”的值,C语言中非0为真,0为假
    • 若表达式为真(非0),执行语句1后退出选择结构;若为假(0),执行语句2后退出。

    (3)常见条件表达式类型

    • 关系表达式:if(x != 0)(x不等于0时为真)、if(a >= b)(a大于等于b时为真)。
    • 逻辑表达式:if(x > 0 && y > 0)(x和y都大于0时为真)。
    • 算术表达式:if(x + 3.5)(x+3.5不等于0时为真)。
    • 变量/常量:if(x)(x非0为真)、if(1)(常量非0为真)、if(0)(常量0为假)。

    (4)示例1:求两个整数的最大值

    #include<stdio.h>
    int main() {
        int a, b, max;
        printf("Input a,b:\n");
        scanf("%d,%d", &a, &b);
        if(a >= b) max = a;  // 条件真时执行
        else max = b;        // 条件假时执行
        printf("max=%d", max);
        return 0;
    }
    

    (5)示例2:求解一元二次方程ax²+bx+c=0的根

    #include<stdio.h>
    #include<math.h>  // 调用平方根函数sqrt需引入
    int main() {
        double a, b, c, disc, x1, x2, p, q;
        scanf("%lf%lf%lf", &a, &b, &c);
        disc = b*b - 4*a*c;  // 计算判别式
        if(disc < 0)
            printf("This equation hasn't real roots.");  // 无实根
        else {
            p = -b/(2.0*a);
            q = sqrt(disc)/(2.0*a);
            x1 = p + q;
            x2 = p - q;
            printf("real roots:\nx1=%7.2lf\nx2=%7.2lf", x1, x2);  // 输出两个实根
        }
        return 0;
    }
    

    2. 条件运算符实现二分支

    (1)基本格式

    表达式1 ? 表达式2 : 表达式3;
    

    (2)执行逻辑

    • 表达式1为真(非0)时,结果为表达式2的值;为假(0)时,结果为表达式3的值。
    • 条件运算符是三目运算符,需搭配3个操作对象,可简化简单的二分支逻辑。

    (3)示例:大写字母转小写字母

    #include<stdio.h>
    int main() {
        char ch;
        scanf("%c", &ch);
        ch = (ch >= 'A' && ch <= 'Z') ? (ch + 32) : ch;  // 是大写则+32转小写,否则不变
        printf("%c", ch);
        return 0;
    }
    

    三、多分支选择结构(多选一)

    多分支结构根据条件的多种结果,从多个操作中选择一个执行,核心实现方式为if嵌套和switch语句。

    1. if嵌套实现多分支

    (1)基本格式

    if (表达式1) 语句1;
    else if (表达式2) 语句2;
    else if (表达式3) 语句3;
    ...
    else 语句n;
    

    (2)关键规则

    • else总是与上方“最近的未配对if”配对,可通过花括号{}强制指定配对关系。
    • 多个if-else if依次判断,满足一个条件后执行对应语句,不再判断后续条件。

    (3)示例1:成绩等级评定(优秀/良好/及格/不及格)

    #include<stdio.h>
    int main() {
        float score;
        printf("Please input a score:\n");
        scanf("%f", &score);
        if(score >= 85) printf("优秀");
        else if(score >= 75) printf("良好");
        else if(score >= 60) printf("及格");
        else printf("不及格");
        return 0;
    }
    

    (4)示例2:分段函数计算(y根据x的范围取不同值)

    // 函数定义:y = -1(x<0);y=0(x=0);y=1(x>0)
    #include<stdio.h>
    int main() {
        int x, y;
        scanf("%d", &x);
        if(x < 0) y = -1;
        else if(x == 0) y = 0;
        else y = 1;
        printf("y=%d\n", y);
        return 0;
    }
    

    2. switch语句实现多分支

    (1)基本格式

    switch (表达式) {
        case 常量1: 语句1; break;
        case 常量2: 语句2; break;
        ...
        case 常量n: 语句n; break;
        default: 语句n+1;  // 可选
    }
    

    (2)核心规则

    • 表达式需为整数类型(int、char等),case后常量需与表达式类型一致,且不能重复。
    • 执行逻辑:找到与表达式值匹配的case,执行对应语句;若无匹配case,执行default语句(若有)。
    • break语句用于跳出switch结构,若无break,会继续执行后续case语句(穿透现象)。
    • 多个case可共用一组执行语句(无break分隔),case顺序不影响结果。

    (3)示例:根据成绩等级输出分数段

    #include<stdio.h>
    int main() {
        char grade;
        printf("Your score:");
        scanf("%c", &grade);
        switch(grade) {
            case 'A': printf("85~100\n"); break;
            case 'B': printf("75~84\n"); break;
            case 'C': printf("60~74\n"); break;
            case 'D': printf("<60\n"); break;
            default: printf("enter data error\n");  // 输入无效等级时执行
        }
        return 0;
    }
    

    (4)共用语句示例

    // A、B、C等级均输出“60分以上”,D等级输出“60分以下”
    switch(grade) {
        case 'A':
        case 'B':
        case 'C': printf("60分以上"); break;
        case 'D': printf("60分以下"); break;
        default: printf("输入错误");
    }
    

    四、综合案例:判断闰年

    1. 闰年规则

    • 能被4整除但不能被100整除,或能被400整除的年份为闰年,否则为平年。

    2. 程序实现

    #include<stdio.h>
    int main() {
        int year, leap = 0;  // leap为标记变量,0表示平年,1表示闰年
        printf("enter year:");
        scanf("%d", &year);
        if(year % 4 == 0) {
            if(year % 100 == 0) {
                if(year % 400 == 0) leap = 1;  // 能被400整除
                else leap = 0;                 // 能被100整除但不能被400整除
            } else leap = 1;                   // 能被4整除但不能被100整除
        } else leap = 0;                       // 不能被4整除
        if(leap) printf("%d is a leap year.", year);
        else printf("%d is not a leap year.", year);
        return 0;
    }
    

    五、核心要求与注意事项

    1. 核心要求

    • 灵活使用if语句解决二分支问题,用if嵌套解决多分支问题。
    • 读懂并合理使用switch语句,理解break的作用和穿透现象。

    2. 注意事项

    • 区分“真”“假”:C语言中无专门布尔类型,非0值(正数、负数、非0小数)为真,0为假。
    • if嵌套中注意if-else配对关系,必要时用{}明确代码块。
    • switch语句中表达式必须为整数类型,case常量不可重复。

    六、思考题

    • C语言中如何表示“真”和“假”?
    • 系统如何判断一个量的“真”和“假”?
  • 251109 答疑

    一、一些前置的小问题

    我们学的是 C 语言还是 C++ 语言?

    认为学的是 C 语言的:课本上写的是 C 语言,而且课上从来没提过 C++;

    认为学的是 C++ 的:我们在 VS 里建工程文件的时候建的是 C++ 文件,而且文件以 .cpp 结尾。

    举例:对比两门语言的 Hello World

    C 语言的 Hello World:

    #include <stdio.h>
    
    int main()
    {
      printf("Hello world!\n");
      return 0;
    }
    

    C++ 语言的 Hello World:

    #include <iostream>
    
    int main()
    {
      std::cout << "Hello world!" << std::endl;
      return 0;
    }
    

    事实上:任何合法的 C 程序都是合法的 C++ 程序,C++ 是 C 语言的一个超集。因此 C 语言的 Hello World 放到 C++ 环境里同样能跑。我们在 VS 里建工程文件的时候建的是 C++ 文件就是基于这个原理。

    为什么 C 语言感觉比 Python 语言复杂很多?

    C 语言感觉更复杂,核心原因是它更贴近硬件、需要手动管理更多细节。Python 帮你封装了这些复杂操作,让你更加能关注于“利用编程解决问题”,而不是把精力花在“编程语言本身的特性”上。

    举例:对比两门语言变量初始化

    对于 C 语言:

    // C
    
    int main()
    {
        int a = 1;
        float b = 1.5;
        return 0;
    }
    

    如果写成 Python 语言:

    # Python
    
    a = 1
    b = 1.5
    
    # Python 其实也可以写成这样
    
    def main() -> int:
        a: int = 1
        b: float = 1.5
        return 0
    
    if __name__ == "__main__":
    	main()
    

    特点:模块化,可复用,通过插件间接实现类型检查

    学习 C 语言时,一定要紧贴上学期计导课程中“信息表示”以及“计算机组成”这两部分知识点,从底层理解 C 语言。

    C/C++ 语言的发展历程、C/C++ 的标准

    C 语言由 Dennis Ritchie 在 1972 年于贝尔实验室开发,主要用于 UNIX 操作系统的开发。主要标准有:

    • K&R C (1978)
    • ANSI C / C89 (1989)
    • C99 (1999)
    • C11 (2011)
    • C17 (2017)

    C++ 由 Bjarne Stroustrup 于 1979 年在贝尔实验室开始设计开发的。作为 C 语言的增强版,C++ 进一步扩充和完善了 C 语言,是一种面向对象的程序设计语言。比较重要的标准有:

    • C++98 (1998)
    • C++11 (2011) – 重大更新,C++ 迈入现代
    • C++20 (2020)
    • C++26 草案 (2025)

    但是为什么 ISO 要专门为 C/C++ 制定标准呢?这是为了保证其可移植性。(举例:条令条例)

    二、程序 1:Hello World

    下面是我们老生常谈的 Hello World 程序:

    #include <stdio.h>
    
    int main()
    {
        printf("Hello world!\n");
        return 0;
    }
    

    我们就来一行一行解释。

    第一行:#include

    #include <stdio.h>
    

    预处理器:#xxx

    以井号 # 开头的称为预处理器。

    预处理器会指定一系列的行为,相当于是给编译器安排了一些活。在编译之前,预处理器就要向编译器明确:你这个编译器需要在编译之前把我指定你干的这些活先干完了,完了以后你再去好好完成你的编译工作。

    既然预处理器会指定一系列行为,那么它能让编译器干什么活呢?这就取决于 # 后面跟的是什么了。下面是一些常用的预处理器:

    • #include:引入头文件。
    • #define:定义宏。
    • #ifdef#ifndef#else#endif:通常用于头文件的编写,大多数情况下是为了防止头文件被多次包含。

    标准库:<xxx.h>

    在标准中规定了一系列标准的库文件,以及库文件中具体要有哪些方法(函数)。在引入标准库的时候使用尖括号 <> 包裹。

    • 例如:使用 #include <stdio.h> 来引入标准库中的 stdio.h 文件

    有的时候,我们还会自己去编写头文件。在引入自己写的头文件的时候使用双引号 "" 包裹。

    • 例如:我编写了一个名为 hello.h 的头文件,就要使用 #include "hello.h"

    由于我们要实现标准输入输出,我们就要借用 stdio.h 标准库。stdio.h 的意思是 Standard Input/Output Header。

    第二行:main 函数

    这一行代码是 main 函数的函数头:

    int main()
    

    程序的入口点

    main 函数是 C 程序的入口点。当程序运行时,操作系统会调用 main 函数开始执行程序。

    返回类型:int

    int 表示 main 函数返回一个整数值。这个返回值通常用来表示程序的退出状态:

    • 0 表示程序成功执行
    • 非零值表示程序执行过程中出现了错误

    有的时候我们会见到 void mian() 的函数头,但不建议这样写。

    参数列表:()

    空的括号表示 main 函数不接受任何参数。在某些情况下,我们也会看到 int main(void) 的写法,两者在 C 语言中是等价的。

    第三行:花括号 {}

    {
        printf("Hello world!\n");
        return 0;
    }
    

    花括号 {} 定义了 main 函数的函数体,是一个代码块,包含了函数要执行的所有语句。

    第四行:printf 函数

    printf("Hello world!\n");
    

    printf 是标准输入输出库中的一个函数,用于向标准输出(通常是终端)打印格式化的文本。

    • "Hello world!\n" 是一个字符串常量
    • \n 是转义字符,表示换行符
    • 每一条语句以分号 ; 结束

    第五行:return 语句

    return 0;
    

    return 语句结束函数的执行,并返回一个值。对于 main 函数,返回 0 表示程序正常结束。

    三、程序 2:简易计算器

    让我们来看一个稍微复杂一点的程序——简易计算器:

    #include <stdio.h>
    
    int main()
    {
        int num1, num2;
        int sum;
        
        // 输入数据
        scanf("%d %d", &num1, &num2);
        
        // 计算结果
        sum = num1 + num2;
        
        // 输出结果
        printf("%d + %d = %d\n", num1, num2, sum);
        
        return 0;
    }
    

    变量定义(声明)

    int num1, num2;
    int sum;
    

    在 C 语言中,使用变量前必须先定义(声明)。这里定义了:

    • 两个整型变量 num1num2 用于存储输入的数字
    • 一个整型变量用于存储加法的结果

    输入函数:scanf

    scanf("%d %d", &num1, &num2);
    

    scanf 函数用于从标准输入读取数据:

    • "%d %d" 是格式字符串,表示要读取两个整数
    • &num1&num2 是变量的地址,& 是取地址运算符
    • 用户输入的两个整数会被存储到 num1num2 变量中

    格式化输出:printf

    printf("%d + %d = %d\n", num1, num2, sum);
    

    printf 的格式说明符:

    • %d – 输出整数
    • %f – 输出浮点数
    • %c – 输出字符
    • %s – 输出字符串
  • 实验 3:我们与计算机的交流方式

    一、实验目标

    1. 熟练运用标准输入输出函数(scanfprintf)处理不同类型数据的输入与输出。
    2. 掌握简单文本文件的读写操作,能编写完整的文件操作程序。

    二、核心知识回顾

    (一)标准输入函数:scanf

    • 语法格式:scanf(格式控制字符串, 变量地址列表)
    • 核心要求:第2至n个参数必须是变量地址(加&)或指向变量地址的指针。
    • 常用格式控制符
      • 整型:%d(有符号十进制)、%u(无符号十进制)、%o(八进制)、%x/X(十六进制)。
      • 实型:%f(小数/指数形式)、%e/E(指数形式)、%g/G(自动选最短格式)。
      • 字符/字符串:%c(单个字符)、%s(字符串)。
    • 附加格式符:l(长整型/双精度)、h(短整型)、域宽(指定输入宽度)、*(读入不赋值)。
    • 关键注意事项
      • 输入数据需与格式控制字符串完全匹配,否则停止读取。
      • 读取整型数据时,遇空格、回车、非小数点符号或字母即结束。
      • %c会读取空格、回车等空白字符,需注意输入顺序。

    (二)标准输出函数:printf

    • 语法格式:printf(格式控制字符串, 输出数据列表)
    • 常用格式控制符:与scanf基本一致,%f默认输出6位小数。
    • 附加格式符:l(长整型)、m(最小输出宽度)、.n(指定小数位数/字符串截取长度)、-(左对齐)。
    • 功能:可输出变量、常量或表达式的值,支持多类型数据同时输出。

    (三)文本文件操作

    • 核心流程:包含头文件(#include <stdio.h>)→ 定义文件指针(FILE *fp)→ 打开文件(fopen)→ 读写操作(fscanf/fprintf)→ 关闭文件(fclose)。
    • 关键函数
      • 打开文件:fp = fopen("文件名", "打开方式"),常用方式有"w"(只写,创建新文件)、"r"(只读,需文件已存在)。
      • 写文件:fprintf(文件指针, 格式字符串, 输出表列),用法与printf类似,多文件指针参数。
      • 读文件:fscanf(文件指针, 格式字符串, 地址表列),用法与scanf类似,需指定文件指针。
      • 关闭文件:fclose(文件指针),避免数据丢失和资源占用。

    三、实验内容与示例代码

    (一)实验1:字符型数据的双重显示

    • 实验要求:定义两个字符型变量,分别用字符方式和整数方式显示,验证字符与ASCII码的对应关系。
    • 示例代码
    #include <stdio.h>
    int main() {
        char c1, c2;
        c1 = 'a';  // 可替换为97、197等数值
        c2 = 'b';  // 可替换为98、198等数值
        printf("%c %c\n", c1, c2);  // 字符方式显示
        printf("%d %d\n", c1, c2);  // 整数方式显示(ASCII码)
        return 0;
    }
    
    • 预期结果:字符初始化时输出对应字符及ASCII码(如a b97 98),超出ASCII范围的数值输出特殊字符及对应整数。

    (二)实验2:多类型数据的输入与输出

    • 实验要求:通过scanf输入指定数据(a=3b=7x=8.5y=71.82c1=Ac2=a),并用printf显示。
    • 示例代码(两种格式控制方式)
    1. 基础格式
    #include <stdio.h>
    int main() {
        int a, b;
        float x, y;
        char c1, c2;
        scanf("%d%d", &a, &b);
        scanf("%f%f", &x, &y);
        scanf("%c%c", &c1, &c2);
        printf("a=%d, b=%d\n", a, b);
        printf("x=%f, y=%f\n", x, y);
        printf("c1=%c, c2=%c\n", c1, c2);
        return 0;
    }
    
    1. 带提示字符格式
    #include <stdio.h>
    int main() {
        int a, b;
        float x, y;
        char c1, c2;
        scanf("a=%d, b=%d,", &a, &b);
        scanf("x=%f, y=%f,", &x, &y);
        scanf("%c,%c", &c1, &c2);
        printf("a=%d, b=%d\n", a, b);
        printf("x=%f, y=%f\n", x, y);
        printf("c1=%c, c2=%c\n", c1, c2);
        return 0;
    }
    

    (三)实验3:文本文件写入

    • 实验要求:创建文本文件,写入整型、实型、字符型数据。
    • 示例代码
    #include <stdio.h>
    int main() {
        int a;
        float b;
        char c;
        FILE *fp;
        scanf("%d,%f,%c", &a, &b, &c);  // 输入格式:123,4.5,n
        fp = fopen("test1.txt", "w");    // 以只写方式创建文件
        fprintf(fp, "%d %f %c\n", a, b, c);  // 写入文件
        fclose(fp);  // 关闭文件
        return 0;
    }
    

    (四)实验4:文本文件读取与显示

    • 实验要求:读取已创建的文本文件数据,显示到屏幕上。
    • 示例代码
    #include <stdio.h>
    int main() {
        int a1;
        float b1;
        char c1;
        FILE *fp;
        fp = fopen("test1.txt", "r");  // 以只读方式打开文件
        fscanf(fp, "%d,%f,%c", &a1, &b1, &c1);  // 按写入格式读取
        printf("a1=%d, b1=%f, c1=%c\n", a1, b1, c1);  // 显示数据
        fclose(fp);  // 关闭文件
        return 0;
    }
    

    四、重难点总结

    (一)重点

    1. scanfprintf的格式控制符匹配与参数使用。
    2. 文本文件“打开-读写-关闭”的完整流程。
    3. 不同类型数据的输入输出格式规范。

    (二)难点

    1. scanf输入时的格式匹配问题,尤其是空白字符的处理。
    2. 文件操作函数的正确调用,包括文件路径、打开方式的选择。

    五、思考题

    1. 为何scanf需要使用变量地址,而printf不需要?
    2. 文件操作结束后,为何必须用fclose关闭文件?
  • 第 3 讲:与计算机面对面地交流

    一、案例引入与核心问题

    • 关键变量定义:编号(code,字符型)、尺寸(length/width/height,浮点型)、速度(speed,整型)、乘员数(passengers,整型)。
    • 两种交流方式:临时交互(键盘+显示器,不保存数据)、持久交互(内存+硬盘,保存数据)。

    二、标准输入输出(键盘与显示器交互)

    (一)scanf语句(数据输入)

    • 功能:通过键盘向变量输入数据,语法为scanf(格式控制字符串, 变量地址列表)
    • 核心要求:变量需加地址符&,指针变量可直接作为参数(无需&)。
    • 常用格式控制符
      • 整型:%d(有符号十进制)、%u(无符号十进制)、%o(八进制)、%x/X(十六进制)。
      • 字符/字符串:%c(单个字符)、%s(字符串)。
      • 实型:%f(小数/指数形式)、%e/E(指数形式)、%g/G(自动选最短格式)。
    • 附加格式符:l(长整型/双精度)、h(短整型)、域宽(指定输入宽度)、*(读入不赋值)。

    (二)printf语句(数据输出)

    • 功能:将变量、常量或表达式的值输出到显示器,语法为printf(格式控制字符串, 输出数据列表)
    • 常用格式控制符:与scanf基本一致,实型%f默认输出6位小数。
    • 附加格式符:m(指定最小输出宽度)、.n(指定小数位数/字符串截取长度)、-(左对齐)。
    • 示例:输出三角形可通过%ms(指定字符串域宽)实现对齐。

    三、文件输入输出(硬盘持久化交互)

    (一)文件基础概念

    • 分类:文本文件(可读字符形式)、二进制文件(二进制数据形式)。
    • 核心类型:FILE(文件类型标识符),通过文件指针(FILE *fp)操作文件。

    (二)文件操作基本流程

    1. 包含头文件:#include <stdio.h>
    2. 定义文件指针:FILE *文件指针名
    3. 打开文件:使用fopen函数,语法为fp = fopen("文件名", "打开方式")
    4. 读写操作:文本文件用fscanf(读)、fprintf(写),用法与scanf/printf类似,多一个文件指针参数。
    5. 关闭文件:fclose(文件指针),避免数据丢失或资源占用。

    (三)fopen打开方式说明

    打开方式 作用 文件不存在时的处理
    “r”(只读) 读取已存在的文本文件 出错
    “w”(只写) 输出数据到文本文件 创建新文件
    “a”(追加) 向文件尾部添加数据 创建新文件
    “r+”(读写) 读写已存在的文本文件 出错
    “w+”(读写) 新建文件并读写 创建新文件
    “a+”(读写) 读写文件,尾部追加 创建新文件

    四、文件读取示例

    • 核心任务:从指定文本文件读取数据并输出到显示器。
    • 关键步骤:定义变量→定义文件指针→打开文件(指定路径)→fscanf读取数据→printf输出→关闭文件。
    • 示例代码逻辑:读取文件中“整数,浮点数,字符”格式数据,分别赋值给对应类型变量并输出。

    五、重点与难点

    (一)重点

    • 标准输入输出:scanfprintf的格式控制符使用规则。
    • 文件操作:文件概念、“打开-读写-关闭”流程及fopenfscanffprintffclose函数用法。

    (二)难点

    • 输入输出缓冲区的理解。
    • 文件操作函数的正确调用(路径、打开方式、指针管理)。

    六、思考题

    1. 如何判断读取的文件是否存在,程序能否自动处理文件不存在的情况?
    2. 若需合并上千个文件,应设计何种解决方案?
  • 实验 2:让计算机学会运算

    一、实验目标

    1. 掌握变量的正确定义与赋值方法。
    2. 熟练运用运算符构建合法的C表达式。
    3. 能够使用顺序结构编写简单C程序。
    4. 熟悉VS2010开发环境,完成程序的创建、编写、运行与调试。

    二、核心知识回顾

    (一)变量基础

    1. 变量使用原则:遵循“先定义,后使用”,未定义的变量无法在程序中调用。
    2. 变量定义格式数据类型 变量名1, 变量名2, ..., 变量名n;
      • 数据类型:决定变量占用的字节数、存储形式及取值范围(如int占4字节,char占1字节)。
      • 变量名:需为合法标识符,由字母、数字、下划线组成,且不能以数字开头(错误示例:int 0_cj;,正确示例:int a, b; float x;)。
    3. 变量初始化:定义时为变量赋初始值(推荐编程习惯,避免内存中随机值干扰),格式为数据类型 变量名 = 初始值;
      • 示例1:int x = 1, y, z;(仅x初始化,y、z未初始化)
      • 示例2:int x = y = z = 1;(x、y、z均初始化为1)

    (二)不同类型数据混合运算规则

    整型(int)、字符型(char)、浮点型(float/double)可混合运算,转换遵循“低精度→高精度”“少字节→多字节”原则,具体优先级(从低到高):char/shortintfloatdouble

    运算示例解析

    已知 int i = 3; float f = 2.5; double d = 7.5;,计算表达式 10 + 'a' + i * f - d / 3

    1. 10 + 'a''a'的ASCII值为97(int型),结果为107int型)。
    2. i * fiint)与ffloat)均转换为double,计算得3*2.5=7.5double型)。
    3. 步骤1结果 + 步骤2结果:107int)转换为double,得114.5double型)。
    4. d / 33int)转换为double,计算得7.5/3=2.5double型)。
    5. 最终运算:114.5 - 2.5 = 112.0double型)。

    (三)简单程序编写步骤

    1. 编写main函数:C程序入口,格式为int main() { ... return 0; }
    2. 定义与初始化变量:根据需求选择合适的数据类型,必要时初始化。
    3. 输入数据:使用scanf函数为变量赋值(如scanf("%f", &r);)。
    4. 实现算法语句:通过表达式、公式等完成核心计算(如圆面积公式、速度位移公式)。
    5. 输出数据:使用printf函数展示结果(如printf("圆面积S=%.6f", s);)。

    三、实验内容与实现

    (一)实验1:观察整型与字符型变量的地址、值与占用空间

    1. 实验需求:存储1个整数和1个字符,观察其存储地址、占用字节数及具体值,支持断点调试与结果打印(选做)。
    2. 参考代码
    #include<stdio.h>
    int main(void)
    {
        int a = 2;    // 初始化整型变量a
        char c = '2'; // 初始化字符型变量c
        // 打印字符变量信息
        printf("字符c的值是%c\n", c);
        printf("字符c的存储地址是0x%x\n", &c);
        printf("字符c占用内存%d个字节\n", sizeof(c));
        // 打印整型变量信息
        printf("整数a的值是%d\n", a);
        printf("整数a的存储地址是0x%x\n", &a);
        printf("整数a占用内存%d个字节\n", sizeof(a));
        return 0;
    }
    
    1. 关键说明
      • sizeof(变量名):获取变量占用的字节数(char占1字节,int占4字节)。
      • &变量名:获取变量的内存地址,打印格式用%x(十六进制)。

    (二)实验2:计算圆的面积与周长

    1. 实验需求:输入圆的半径r,根据圆周率3.14计算面积S(公式:S=3.14*r*r)与周长C(公式:C=2*3.14*r)。
    2. 参考代码
    #include<stdio.h>
    int main()
    {
        float r, s, c; // 定义半径r、面积s、周长c(浮点型)
        printf("请输入圆半径r:");
        scanf("%f", &r); // 输入半径
        s = 3.14 * r * r; // 计算面积
        c = 2 * 3.14 * r; // 计算周长
        printf("圆面积S=%.6f\n", s); // 保留6位小数输出面积
        printf("圆周长C=%.6f\n", c); // 保留6位小数输出周长
        return 0;
    }
    
    1. 运行示例:输入3.5,输出“圆面积S=38.465000,圆周长C=21.980000”。

    (三)实验3:计算物体的速度与位移

    1. 实验需求:输入初速度v0(浮点型)、加速度a(浮点型)、时间t(整型),根据物理公式计算t时刻的速度与位移。
    2. 核心公式
      • 速度:v = v0 + a * t
      • 位移:s = v0 * t + 0.5 * a * t * t(用0.5避免整数除法精度问题)
    3. 参考代码
    #include <stdio.h>
    int main(void)
    {
        float v0, a; // 初速度v0、加速度a(浮点型)
        int t;       // 时间t(整型)
        float v, s;  // t时刻速度v、位移s(浮点型)
        
        printf("请输入初始速度、加速度和经过的时间:\n");
        scanf("%f%f%d", &v0, &a, &t); // 输入三个参数
        
        v = v0 + a * t;                  // 计算速度
        s = v0 * t + 0.5 * a * t * t;    // 计算位移
        
        printf("当初始速度为%f时\n", v0);
        printf("%.1fs时的速度为%f\n", t, v); // 时间t保留1位小数
        printf("%.1fs时的位移为%f\n", t, s);
        return 0;
    }
    
    1. 运行示例:输入0 0.19 30,输出“当初始速度为0.000000时,30.0s时的速度为5.700000,30.0s时的位移为85.500000”。

    (四)实验4(选做):计算平面两点间距离

    1. 实验需求:输入两点坐标(x1,y1)(x2,y2),计算两点间距离(保留2位小数)。
    2. 核心公式distance = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1))(需调用math.h头文件的sqrt函数)。
    3. 参考代码
    #include<stdio.h>
    #include<math.h> // 包含sqrt函数的头文件
    int main() 
    {
        float x1, y1, x2, y2; // 两点坐标
        float distance;        // 两点间距离
        
        scanf("%f%f%f%f", &x1, &y1, &x2, &y2); // 输入坐标
        // 计算距离的平方
        distance = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
        distance = sqrt(distance); // 开平方得距离
        
        printf("%.2f", distance); // 保留2位小数输出
        return 0;
    }
    
    1. 运行示例:输入3 5 6 7,输出“3.61”。

    四、程序调试(VS2010环境)

    (一)调试核心步骤

    1. 设置断点:在需观察的代码行左侧单击,出现红色圆点(如变量初始化行)。
    2. 启动调试:点击“调试”→“启动调试”(或按F5),程序运行至断点处暂停。
    3. 观察变量
      • 打开“局部变量”窗口,查看变量的实时值。
      • 若需查看地址或自定义变量,使用“快速监视”(按Shift+F9),输入&变量名查看地址。

    (二)常用调试快捷键

    功能 快捷键
    启动调试 F5
    停止调试 Shift+F5
    逐语句执行(进入函数) F11
    逐过程执行(跳过函数) F10
    跳出当前函数 Shift+F11
    切换断点 F9
    快速监视 Shift+F9

    五、思考题

    1. 如何在程序运行时测试多组数据以验证正确性?(提示:可结合循环结构实现)
    2. 能否让程序从文件中读取数据,或把运行结果保存到文件中?(提示:需使用文件操作函数)