实验二 STC8H8K64U程序控制结构与函数综合实验

📋 实验二 STC8H8K64U程序控制结构与函数综合实验

一、实验目的

  1. 掌握顺序结构程序的设计方法,理解语句按书写顺序执行的特性。
  2. 掌握选择结构if-elseswitch-case)的语法格式及应用场景。
  3. 掌握循环结构whiledo-whilefor)的三种形式及其区别。
  4. 掌握函数的声明、定义和调用方法,理解模块化编程思想。
  5. 学会在无串口输出条件下通过软件仿真观察变量变化,验证程序逻辑。

二、实验设备与软件环境

1、硬件:STC8H8K64U单片机开发板(含USB下载电路)、USB数据线。

2、软件:

  • Keil C51(支持STC8H系列)集成开发环境。
  • STC-ISP下载软件(用于程序烧录)。

三、实验原理

3.1 顺序结构

程序按照代码书写顺序从上到下逐条执行,没有分支和循环。例如赋值、算术运算等语句。

3.2 选择结构

  1. if-else:根据条件表达式的真假执行不同分支。可嵌套或多分支(else if)。
  2. switch-case:基于整型表达式的值跳转到对应case执行,常用于多分支等值判断。

3.3 循环结构

  1. while:先判断条件,条件为真时执行循环体,可能一次都不执行。
  2. do-while:先执行一次循环体,再判断条件,至少执行一次。
  3. for:集初始化、条件判断、循环变量更新于一体,适用于已知循环次数的情况。

3.4 函数

  1. 声明:告诉编译器函数名、返回值类型和参数类型。
  2. 定义:提供函数的具体实现。
  3. 调用:通过函数名和实参执行函数体,可获得返回值。

四、实验内容

分析并运行提供的参考程序,该程序演示了以下内容:

  1. 顺序结构:定义整型变量a、b、c、d,依次进行加法和乘法运算。
  2. 选择结构:
    • 用 if-else 根据成绩 score 划分等级 level
    • 用 switch-case 根据 option 的值给 resultSwitch 赋值。
  3. 循环结构:
    while 计算1~10的和。
    do-while 计算5的阶乘。
    for 遍历数组 arr 并求和。
  4. 函数:
    • 声明并定义加法函数 add、偶数判断函数 isEven
    • 定义 demoFunctionCall 函数,内部调用 addisEven
    • 在 main 中调用上述函数,并使用 addResultevenCheck 接收返回值。

五、实验步骤

5.1 创建工程并编写代码

  1. 打开Keil软件,新建工程,选择单片机型号STC8H8K64U(若无此型号,可先选任意8051,然后通过STC-ISP添加头文件)。
  2. 新建源文件 main.c,将实验代码完整输入(注意代码中 for (int k=0;... 若编译器报错,可将 int k 提前到函数开头定义,如 int k;for(k=0;...)
  3. 工程配置:生成HEX文件(Output → Create HEX File)。

5.2 编译与语法检查

点击编译(Build),确保无错误、无警告。若提示 stc8h.h 找不到,请从STC官方下载并复制到Keil安装目录的 C51/INC 文件夹下,或改用 #include <reg51.h>(但建议使用官方头文件)。

5.3 仿真调试(观察变量值)

由于程序无串口输出,推荐采用软件仿真观察各变量变化:

  1. 进入Keil调试模式(Debug → Start/Stop Debug Session)。
  2. 在main函数入口处设置断点(双击代码行左侧灰色区域)。
  3. 单步执行(F11),依次观察变量窗口(Watch Window)中以下变量的变化:
    顺序结构:a、b、c、d
    选择结构:score、level、option、resultSwitch
    循环结构:sumWhile、product、sumFor
    函数相关:addResult、evenCheck
  4. 验证每个变量是否按预期变化。例如c应为15,d应为30,level应为'B'(ASCII 66),resultSwitch应为20,sumWhile应为55,product应为120,sumFor应为30,addResult应为300,evenCheck应为1。

5.4 硬件下载与运行(可选)

  1. 使用STC-ISP软件,选择单片机型号STC8H8K64U,打开生成的HEX文件。
  2. 开发板上电,通过USB或串口线连接,点击“下载/编程”。
  3. 下载成功后程序自动运行。虽无输出外设,但可通过在while(1)中添加LED闪烁代码(例如 P10=~P10; delay();)来验证程序正常运行。

5.5 修改与扩展练习

  1. if-else 的分数段改为90以上'A',80~89为'B',70~79为'C',60~69为'D',其余为'E',修改代码并仿真验证。
  2. switch 的option值改为3,观察resultSwitch的变化。
  3. for 循环改写 while 的1~10求和,比较两种写法的异同。
  4. 编写一个函数 int factorial(int n),计算n的阶乘,并在main中调用它计算6!。

六、实验结果与分析

6.1 预期结果(仿真观察值)

变量名 初始值 操作后值
c 未初始化 15
d 未初始化 30
level 未初始化 'B' (ASCII 66)
resultSwitch 0 20
sumWhile 0 55
product 1 120 (1×2×3×4×5)
sumFor 0 30 (2+4+6+8+10)
addResult 未初始化 300 (100+200)
evenCheck 未初始化 1 (300是偶数)
finalCheck 0 15+30+66+20+55+120+30+300+1 = 637

6.2 分析

  • 顺序结构:变量c和d严格按书写顺序计算,结果正确。
  • 选择结构:score=85满足score>=75,执行level='B';option=2匹配case 2,resultSwitch=20。
  • 循环结构:
    • while 循环累加1~10,循环变量i从1递增至11时退出。
    • do-while 循环先执行一次 product*=j,j从1到5,得到120。
    • for 循环用k作为下标遍历数组,累加元素值。
  • 函数调用:add(100,200)返回300,isEven(300)返回1;demoFunctionCall内部局部变量dummy不影响外部。
  • 防优化:volatile修饰的finalCheck强制编译器保留计算过程,便于仿真观察。

七、思考题

1. do-while循环与while循环的根本区别是什么?本实验中如果将计算阶乘的do-while改为while,需要修改哪些地方?

答:do-while至少执行一次循环体,while可能一次也不执行。改为while时需先初始化 j=1, product=1,然后 while{product*=j; j++;},结果相同。

2. 在switch语句中,如果省略break会发生什么?请结合本实验的option=2举例说明。

答:省略break会发生“穿透”,即执行完匹配的case后继续执行后续case的代码。例如本实验中若case 2后无break,则执行 resultSwitch=20 后再执行 case 3resultSwitch=30,最终结果为30。

3. 函数的声明和定义可以放在调用之后吗?本实验中将add的定义放在main之后,但程序仍能编译,为什么?

答:可以,但必须在调用前进行声明。本实验在文件开头声明了 add,所以定义可以放在main之后。若既无声明也无定义(完全在调用后定义),则编译报错。

4. 为什么要使用volatile关键字修饰finalCheck?如果不使用,在Keil默认优化级别下可能发生什么?

答:volatile告诉编译器该变量可能被外部因素改变(实际此处为防止优化),避免编译器将未被使用的变量或计算删除。无volatile时,由于finalCheck后续未被使用,编译器可能优化掉整个计算过程,导致仿真时看不到正确结果。

八、实验报告要求

  1. 简述实验目的及实验原理。
  2. 粘贴完整的实验代码,并在关键行添加注释,说明顺序、选择、循环结构和函数的实现。
  3. 记录仿真调试中观察到的各变量值(可截图或列表),与预期结果对比。
  4. 回答“思考题”中的任意两个问题。
  5. 总结实验中遇到的问题(如语法错误、仿真观察技巧等)及解决方法,写出心得体会。

📎 附件:实验代码

#include <stc8h.h>

// 函数声明
int add(int a, int b);
int isEven(int num);
void demoFunctionCall(void);

// 加法函数
int add(int a, int b) {
    return a + b;
}

// 判断偶数函数
int isEven(int num) {
    if (num % 2 == 0)
        return 1;
    else
        return 0;
}

// 演示函数调用的函数
void demoFunctionCall(void) {
    int x, y, sum, evenFlag;
    volatile unsigned char dummy;   /* C89 要求所有变量在最前面声明 */

    x = 7;
    y = 8;
    sum = add(x, y);
    evenFlag = isEven(sum);
    dummy = (unsigned char)(sum + evenFlag);
}

void main(void) {
    /* ========== C89 风格:所有变量声明放在函数开头 ========== */
    int a, b, c, d;
    int score;
    char level;
    int option, resultSwitch;
    int sumWhile, i;
    int product, j;
    int arr[5];
    int sumFor, k;
    int addResult, evenCheck;
    volatile unsigned long finalCheck;

    /* ========== 1. 顺序结构 ========== */
    a = 5;
    b = 10;
    c = a + b;
    d = c * 2;

    /* ========== 2. 选择结构 ========== */
    /* 2.1 if-else */
    score = 85;
    if (score >= 90) {
        level = 'A';
    } else if (score >= 75) {
        level = 'B';
    } else {
        level = 'C';
    }

    /* 2.2 switch */
    option = 2;
    resultSwitch = 0;
    switch (option) {
        case 1:  resultSwitch = 10; break;
        case 2:  resultSwitch = 20; break;
        case 3:  resultSwitch = 30; break;
        default: resultSwitch = 0;  break;
    }

    /* ========== 3. 循环结构 ========== */
    /* 3.1 while 循环 */
    sumWhile = 0;
    i = 1;
    while (i <= 10) {
        sumWhile += i;
        i++;
    }

    /* 3.2 do-while 循环 */
    product = 1;
    j = 1;
    do {
        product *= j;
        j++;
    } while (j <= 5);

    /* 3.3 for 循环 */
    arr[0] = 2; arr[1] = 4; arr[2] = 6; arr[3] = 8; arr[4] = 10;
    sumFor = 0;
    for (k = 0; k < 5; k++) {
        sumFor += arr[k];
    }

    /* ========== 4. 函数调用 ========== */
    demoFunctionCall();
    addResult = add(100, 200);
    evenCheck = isEven(addResult);

    /* 防止优化,汇总结果 */
    finalCheck = c + d + (unsigned int)level + resultSwitch + sumWhile
                 + product + sumFor + addResult + evenCheck;

    /* 主循环 */
    while (1) {
        /* 空转 */
    }
}
💡 提示:将上述代码复制到Keil工程中编译调试,注意头文件路径和C89标准变量声明位置。仿真时务必使用Watch Window跟踪变量值,验证程序逻辑。

本站所有文章、数据、图片来源于网络,仅供学习使用,如有侵权,联系删除!