01 / 目标
学完后,你能做到什么?
先给自己一个明确的终点。本课不要求记住每个编译器参数,但要求你能独立重做完整过程。
- 确认电脑上有可用的 C 编译器,并在终端看见它的版本。
- 亲手保存一个真正以
.c 结尾的源文件,编译后运行出 Hello, world!。
- 解释
#include、main、printf、\n 和 return 0 的职责。
- 发生错误时,能区分“编译没通过”“程序没找到”和“运行结果不对”。
本课产出一个 hello.c 文件、一个编译出的可执行文件,以及一份自己的小记录:运行了什么命令、看到了什么输出、遇到过什么错误。
02 / 环境
先让编译器能被找到
编写 C 代码可以用普通文本编辑器或 VS Code;编辑器负责写文件,编译器负责把文件变成程序。选择与你的电脑相符的一条路径即可,不需要同时安装两套环境。
遇到 gcc: command not found?先确认安装步骤确实完成,并确认自己正在正确的终端中;不要急着修改 C 代码。环境问题和语法问题要分开排查。
03 / 源码
亲手建立 hello.c
在一个容易找到的文件夹中,新建纯文本文件 hello.c。把下面的代码完整输入并保存。为了把注意力放在 C 语言本身,第一版只输出英文字母,暂时避开不同终端的中文编码差异。
hello.cC11 · 可直接编译
#include <stdio.h>
int main(void)
{
printf("Hello, world!\n");
return 0;
}
保存前检查:文件名应是 hello.c,不是 hello.c.txt;代码中的引号是英文半角双引号 ",分号是英文半角 ;。文本编辑器的“另存为”窗口有时会自动加上 .txt,请特别看一眼实际文件名。
04 / 执行
编译一次,再运行一次
让终端切换到保存 hello.c 的文件夹。先用 ls 确认当前目录能列出 hello.c,再使用对应平台的命令。这里每个平台都有两条不同的命令:第一条生成程序,第二条运行程序。
预期输出一行文本
Hello, world!
如果编译命令没有报错,它通常会安静地结束,并生成可执行文件。-std=c11 选择 C11 语言模式;-Wall 和 -Wextra 打开两组常用警告;-o 后面是你指定的输出文件名。警告不是“已修好”,应读懂并处理后再往下走。
第一次成功后,马上再试一次把源码中的 world 改成自己的英文名字,保存、重新运行编译命令,然后再运行程序。只改源文件却直接启动旧的可执行文件,屏幕仍会显示旧结果。
05 / 阅读
逐行读懂这七行
别急着背“固定模板”。把每一行放回它要解决的问题中,就能理解为什么程序这样写。
#include <stdio.h>- 我要调用谁?
stdio.h 是标准输入输出头文件,里面提供了 printf 等函数的声明,让编译器知道我们准备怎样调用它。#include 是预处理指令;它并不是把整个标准库的实现代码复制进这个文件。函数实现的使用还涉及后面的链接步骤。
int main(void)- 程序从哪里开始?在这类由操作系统启动的普通 C 程序中,
main 是入口函数。int 表示它最终返回一个整数状态,括号里的 void 表示这个版本不接收参数。后面学 STM32 时会认识启动代码如何在进入应用 main 前准备硬件。
{ 和 }- 哪些语句属于函数?花括号把函数体包在一起。从左花括号开始执行里面的语句,到右花括号结束。它们要成对出现;代码缩进方便人看,不代替花括号的结构作用。
printf("Hello, world!\n");- 程序做了什么?
printf 把双引号中的字符串输出到标准输出。这里的 \n 是换行转义序列,不是屏幕上要逐字打印的两个字符。右括号后面的英文分号 ; 结束这条语句;漏掉它会产生编译诊断。
return 0;- 程序怎样结束?
return 使 main 返回。约定上,状态 0 表示本次运行成功。先明确写出来,便于建立“程序可以向外报告结果”的概念。
空行与缩进:源码第二行是空行,四个空格是缩进,它们让结构更清楚。编译器关心的是语言结构;良好的排版主要帮助你和以后一起读代码的人。
06 / 原理
从文本到可执行文件
hello.c 只是人写的文本。它不能靠双击源码直接变成程序。使用 gcc 命令时,GCC 驱动会协调预处理、编译、汇编和链接;本课的一条编译命令把这些步骤串起来了。
- 01
源码你保存的 hello.c,包含预处理指令和 C 语句。
- 02
预处理处理 #include 等指令,准备送去编译的文本。
- 03
编译与汇编把 C 代码转换为目标文件;目标文件还不是这次要运行的完整程序。
- 04
链接把需要的目标代码与库代码联系起来,生成 hello.exe 或 hello。
- 05
运行操作系统启动可执行文件;此时 printf 才真正执行并显示文字。
先分清两个动作:gcc ... -o ... 是构建程序;./hello 或 ./hello.exe 是运行已经构建好的程序。前者通过不代表后者输出一定符合你的预期,所以每次修改都要重新编译并运行。
想看一眼“只编译、不链接”是什么样?(选做)
在 hello.c 所在目录运行以下命令。-c 让 GCC 先停在目标文件阶段;此时会生成 hello.o,还没有生成最终程序。第二条命令再把目标文件链接成可执行文件。
gcc -std=c11 -Wall -Wextra -c hello.c -o hello.o
gcc hello.o -o hello
这组示例以 Ubuntu 的输出名 hello 演示;在 Windows UCRT64 可把最后的输出名改成 hello.exe。
07 / 排错
出了错,先判断卡在哪一步
报错文字会随编译器版本和系统略有变化。先看错误发生在环境、文件、编译、链接,还是运行,再回到对应位置修改。
| 看到的现象 | 先检查什么 | 下一步 |
gcc 找不到 | 是否安装 GCC;Windows 是否打开的是 UCRT64 终端。 | 回到本课环境步骤,先让 gcc --version 成功。 |
hello.c: No such file or directory | 终端所在文件夹、实际文件名是否为 hello.c。 | 用 pwd 和 ls 核对目录;检查是否被保存为 hello.c.txt。 |
提示缺少 ;、引号或花括号 | 从报错行及其上一行看是否漏写配对符号。 | 对照完整示例修正后,重新运行编译命令。 |
找不到 main | 是否写成别的函数名,或编译的不是当前这份源码。 | 确认 int main(void) 与命令中的文件名一致。 |
运行时提示找不到 hello | 编译是否真的成功;当前目录是否有输出文件。 | 先用 ls 看文件,再输入带 ./ 的运行命令。 |
| 程序仍输出旧文字 | 修改源码后是否保存、是否重新编译。 | 保存 → 编译 → 运行,按这个顺序重做。 |
面对 warning 怎么办?Warning 表示编译器发现值得注意的问题;它有时仍会生成程序。不要把“生成了文件”当成“代码已经正确”。先读完整诊断、定位源文件行号,再处理或解释警告。
08 / 练习
做三次小修改,而不是只读一遍
每题都先预测结果,再改代码、保存、编译、运行。展开答案前把自己的判断写下来;即使答对,也看一眼编译器有没有提示。
LEVEL 01 / 改动
换一句问候
把输出改为 Hello, C!。除了字符串里的文字,还有什么必须保留?运行后应看见几行?
查看参考答案
printf("Hello, C!\n");
保留双引号、右括号和分号;\n 让下一次输出从新的一行开始。这个程序仍只输出一行问候。
LEVEL 02 / 预测
连续输出两次
在 main 中依次放两条 printf:第一条输出 C 和换行,第二条输出 STM32 和换行。先画出你预期的屏幕内容,再运行验证。
查看参考答案
printf("C\n");
printf("STM32\n");
应看到两行:第一行 C,第二行 STM32。\n 是两行分开的关键;去掉第一条中的 \n,结果会变成同一行的 CSTM32。
LEVEL 03 / 排错
故意制造一次编译错误
暂时删掉 printf("Hello, world!\n") 末尾的分号并编译。记录编译器指出的文件名、行号和问题,再补回分号,重新编译、运行。错误信息不必与你看到的示例一字不差。
查看排错思路
缺少分号时,编译器会在这一句附近给出语法诊断,有时箭头指向下一行。修复后的语句应为:
printf("Hello, world!\n");
排错的目的不是背报错原文,而是练习把诊断定位到自己的代码,再用新一轮编译验证修复。
09 / 验收
什么时候可以进入下一课?
不用赶进度。下面四项可以独立完成时,本课就算过关:
- 关闭旧终端后重新打开,在正确的环境中运行
gcc --version。 - 从空文件重新敲出
hello.c;编译无错误和未解释的警告,输出与预期一致。 - 不用看答案,口头解释
main、printf、\n、return 0 以及 #include 的作用。 - 修改输出后,自己完成“保存 → 编译 → 运行”,并能用错误信息修复一个小拼写错误。
和嵌入式路线有什么关系?日后写 STM32 程序,同样要把源码构建成目标文件和最终映像,也会读编译警告、定位错误。现在先在电脑上把这一套最短流程练熟,再逐步加入变量、控制流和硬件细节。
资料
从官方文档继续查
本课以“能读懂并运行”为目标。以后遇到具体编译器版本或开发板差异,优先核对所用工具链与板卡的官方文档。