↓ 跳过正文
  1. 嵌入式学习笔记/
  2. EmbeddedStudy/

第一个 C 程序:按顺序得到确定结果

目录
EmbeddedStudy - 这篇文章属于一个选集。
§ 2: 本文

本文由 AI 辅助生成并经自动审查;尚未完成真实硬件验证。涉及具体芯片、时序和电气行为时,请以文末第一方资料和实际测试为准。

第 2 课:第一个 C 程序:按顺序得到确定结果
#

本课只建立一个核心概念:最小 C 程序按语句顺序执行,并得到确定结果。我们先在普通 Windows 主机上验证逻辑;没有开发板、目标工程或运行记录,因此这不是硬件验证。

1. 本节目标
#

完成本课后,你应该能在主机上阅读并修改一个单文件 C11 子集程序,说明它怎样按语句顺序完成一次确定计算,并通过程序的退出状态观察结果;同时能说明主机结果不能证明程序已经在嵌入式目标上运行。

预计用时 30 分钟。

2. 它解决什么问题
#

第一次看到 C 代码时,很容易只盯着符号,却不知道运行时先发生什么、后发生什么。本课把问题缩小到三个可观察的问题:

  1. 程序从哪里开始执行本课代码?
  2. 每条语句怎样改变当前结果?
  3. 程序结束后,怎样在不依赖硬件的情况下看到结果?

当前没有具体开发板,所以实验不能依赖引脚、时钟、外设或调试器。我们用主机的退出状态承载一个小范围结果,只验证顺序计算逻辑。

3. 必要前置知识
#

已完成的上一课提供两个结论:

  • 主机与嵌入式目标的运行环境不同;
  • 源文件 -> 构建产物 -> 运行 -> 可观察结果 才是最小验证闭环。

本课不假定你已经学过 C 语法。你只需要一台 Windows 主机、PowerShell,以及能在终端调用的 GCC C 编译器。先运行:

gcc --version

若系统提示找不到 gcc,说明实验依赖尚未满足,应先配置编译器;这不能用来判断示例代码是否正确。

4. 核心原理
#

最小程序不是“一次同时做完所有事情”,而是从 main 中第一条语句开始,一条接一条执行。前一条完整表达式产生的结果会在程序进入后续代码前生效;赋值会先计算右侧,再把结果保存到左侧指定的位置。[1]

  • 解决什么问题:让我们能预测程序每一步以及最终结果,而不是凭运行后的数字猜测。
  • 对象在哪里定义:要执行的语句和初始值都写在 main.c 的 main 中。
  • 谁持有它:源文件保存文字规则;程序运行时,当前的 result 值由这个正在运行的主机程序持有。
  • 谁注入它:本例没有外部输入;学习者把初始值 4 直接写入源文件,主机负责启动构建产物。
  • 调用如何流动:主机启动 lesson.exe,执行进入 main,语句依次把 result 从 4 变为 7,main 返回 7,PowerShell 再读取退出状态。

这里的“依次”是本课重点。若交换会依赖前一步结果的语句,最终结果可能改变;若源文件和输入不变,这个简单计算的预期结果也不变。

5. 关键术语与直观模型
#

术语 本课中的准确含义
源文件 保存 C 程序文本的文件,本课名为 main.c。
main 此最小主机程序开始执行本课代码时使用的函数名称;目标启动过程以后再学。
语句 让程序完成一个小动作的 C 代码单元,通常以分号结束。
返回值 main 结束时交给主机的整数结果。
构建产物 根据源文件生成、可由当前主机运行的程序文件,本课名为 lesson.exe。
退出状态 程序结束后由主机保存、可被 PowerShell 读取的数值。
Host Fake(主机替身) 没有目标硬件时,在主机上替代硬件依赖以验证逻辑的方法。

把 result 想成纸上的一个格子:第一条语句写入 4,第二条语句取出旧值、加 3,再把 7 写回同一个格子。result 是变量,也就是运行时可保存一个值的命名位置。

6. 从输入到结果的完整流程
#

  1. 学习者把程序文字保存为 main.c。
  2. GCC 读取源文件,生成主机构建产物 lesson.exe。本课只使用这个整体能力,不展开内部处理阶段。[2]
  3. PowerShell 请求 Windows 启动 lesson.exe。
  4. 程序进入 main,先执行 int result = 4;,此时 result 为 4。
  5. 接着执行 result = result + 3;,先使用旧值 4 计算,再保存新值 7。
  6. 最后执行 return result;,main 返回 7,进程结束,并把这个小范围值作为退出状态报告给父进程。[1]
  7. PowerShell 中的 $LASTEXITCODE 显示最近一次本机程序的退出状态,预期为 7。

本实验特意不打印文本:这样可以集中观察“计算结果 -> 返回值 -> 退出状态”这一条路径。

7. 嵌入式系统中的对应位置
#

源文件中的顺序计算可以迁移到嵌入式程序,但运行边界不同。STM32 官方开发工具文档把工程构建、运行与调试放在面向目标设备的开发流程中;主机上的 .exe 不能代替目标构建产物或下载、调试证据。[3]

以后有具体开发板时,可以在目标工程的适当位置执行同样的纯计算,并用调试器观察变量。但从处理器启动到进入目标程序 main 的过程不等同于 Windows 启动 .exe,本课不展开,也不猜测具体芯片配置。

8. 主机实验与硬件迁移边界
#

本例没有 GPIO 等硬件依赖,因此只是主机逻辑验证,没有引入实际的 Host Fake。以后代码依赖 GPIO、传感器等硬件接口时,才需要用可控的主机对象替代那些依赖。当前实验只能证明:为当前主机构建并实际运行的程序,按预期顺序得到退出状态 7。

它不能证明:

  • 代码已经为 STM32 构建;
  • 固件已经下载到开发板;
  • 任一引脚、时钟或外设工作正常;
  • F4、F7、H5 或 H7 上存在相同的启动和观察方式。

硬件迁移时应另外保存目标构建、下载和调试观察证据。当前 hardware_verified=false。

9. 最小代码示例
#

将以下内容保存为 main.c:

int main(void)
{
    int result = 4;
    result = result + 3;

    return result;
}

这是单文件 C11 子集示例,不使用动态内存。代码属于主机逻辑验证层,依赖方向只有“主机启动程序并接收返回值”。本课不讨论 int 的宽度、表示范围或溢出,这些属于下一主题 integer-model。

状态说明:示例,待主机验证;若迁移到硬件,仍待上板验证。

10. 常见错误
#

  • 从下往上读代码:运行顺序是进入 main 后从第一条语句向后推进。
  • 把第二行算成 3:右侧 result 使用前一步保存的 4,所以新值是 7。
  • 修改源文件后直接运行旧程序:旧的 lesson.exe 不会自动包含新文字,必须重新构建。
  • 只运行 $LASTEXITCODE:它表示最近一次本机程序的状态,应紧接着运行 lesson.exe 后读取。
  • 把 7 当成硬件证据:它只来自 Windows 主机进程。

11. 调试观察点
#

按流程定位问题:

  1. gcc --version 是否能显示编译器信息。
  2. 当前目录是否存在刚保存的 main.c。
  3. 构建命令是否报告错误,目录中是否出现新的 lesson.exe。
  4. 执行 lesson.exe 后是否立即读取 $LASTEXITCODE。
  5. 若结果不是 7,核对初始值、加数和是否在修改后重新构建。

有主机调试器时,可选地逐语句执行并观察 result 依次为 4、7;本课不要求安装或使用特定调试器。

12. 实战实验
#

在含有 main.c 的目录打开 PowerShell,逐条执行:

gcc -std=c11 -Wall -Wextra -pedantic main.c -o lesson.exe
.\lesson.exe
$LASTEXITCODE

预期最后一条命令显示:

7

然后只把初始值改成 5,先在纸上预测结果,再重新构建和运行。预期退出状态为 8。最后把加数改成 4,再次先预测、后验证,预期为 9。

在真实运行记录产生前,以上结果都是预期值,不能声称已经编译或运行成功。实验完成后应保留命令、编译器版本、实际输出和时间,才能形成主机验证证据。

13. 自检题
#

  1. result 为什么在第二条语句之后是 7?
  2. return result; 之后,哪个主机机制让结果可观察?
  3. 修改 main.c 后为什么要重新构建?
  4. 如果初始值为 6、加数为 2,退出状态应是多少?
  5. 为什么主机上看到预期结果仍不能设置 hardware_verified=true?

14. 面试题
#

问:如何解释这个最小 C 程序从启动到结果可见的完整流动?

答题要点:Windows 启动主机构建产物;执行进入 main;语句按书写顺序更新局部变量;main 返回小范围整数;主机保存退出状态;PowerShell 读取该状态。还应主动说明,这不是 Cortex-M 启动调用链或上板证据。

15. 延伸思考
#

  • 若把“加 3”放到“写入初始值 4”之前,程序还具备当前相同的清晰含义吗?为什么?
  • 如果程序中有两次连续加法,怎样在纸上记录每条语句之后的值?
  • 未来有硬件后,除了退出状态,还能通过哪些通用手段观察计算结果?只列类别,不假定具体外设。

16. 本节总结
#

最小 C 程序从 main 中的第一条语句开始,按顺序完成小动作。前一步保存的值可以成为后一步的输入,最后由 main 的返回值进入主机退出状态。本课用 4 -> 7 -> 退出状态 7 建立了可手工预测、可主机观察的最小逻辑闭环。

17. 下一步
#

下一课应由课程规划决定是否进入 integer-model,系统学习整数的宽度、范围与表示规则。本课生成本身不代表已经掌握;只有自检答案、实验记录或复习结果才能提高掌握度。

18. 参考资料
#

  1. GNU Project, GNU C Language Introduction and Reference Manual, version 0.1, 5 May 2025,访问日期:2026-09-21。核对位置:Simple Assignment、Sequence Points、Returning Values from main;用于支撑赋值、完整表达式之间的执行顺序以及 main 返回值成为退出状态。
  2. GNU Project, Using the GNU Compiler Collection (GCC): Invoking GCC, GCC 14.2.0,访问日期:2026-09-21。用于核对 GCC 调用方式与从输入文件生成程序的整体表述。
  3. STMicroelectronics, STM32CubeIDE user guide, UM2609 Rev 18, June 2026,访问日期:2026-09-21。用于核对 STM32 目标工程的构建、运行与调试背景,以及主机验证与目标工作流的边界。
RuChen
作者
RuChen
EmbeddedStudy - 这篇文章属于一个选集。
§ 2: 本文