C 语言第 1 章:概述——从机器语言到第一个 Hello World 的完整流程

写于 2026-11-01

很多教材把第一章写成"发展史 + 特点列表",学生读完只记住几个年份。这一章真正要建立的是一个分层视角:你敲下的那行 printf,要经过预处理、编译、汇编、链接四道工序,才变成 CPU 能执行的二进制指令。理解这条链路,后面遇到 undefined reference、implicit declaration 这类报错时,才知道该去哪一环找原因。本章还要解决一个非常实际的问题——工具怎么装、第一个程序怎么跑通。环境不通,后面每一章都是纸上谈兵。

一、C 语言的来历与特点

C 语言 1972 年诞生于贝尔实验室,作者是 Dennis Ritchie。它的直接动因很务实:当时的 UNIX 操作系统用汇编写,换一台机器就要重写一遍,Ritchie 需要一个既能操作硬件、又能跨机器移植的语言。它的前身是 Ken Thompson 的 B 语言,B 又源自 BCPL。1978 年 Kernighan 与 Ritchie 合著的《The C Programming Language》确立了事实标准(K&R C);1989 年 ANSI 发布 C89,1990 年被 ISO 采纳为 C90;此后有 C99、C11、C17、C23。

C 的特点可以用四句话概括:

  • 简洁。C89 只有 32 个关键字(C99 增至 37 个,C11 为 44 个),语言本身很小,大量功能交给标准库。
  • 贴近硬件。有指针,能直接读写内存地址,能位运算,这是它能写操作系统内核的根本原因。
  • 高效。编译产物接近手写汇编的质量,至今仍是性能敏感领域的首选。
  • 可移植。标准规定了语法与语义,同一份源码换平台重新编译即可(前提是不碰平台相关的库)。

它也有明显的代价:不做运行时检查——数组越界、空指针解引用、内存泄漏,编译器大多不拦,程序直接崩溃或悄悄算错。这正是后面每一章都要反复强调"边界"的原因。

它的主战场是操作系统内核、嵌入式与驱动、编译器与解释器、数据库引擎、高性能库的底层。学会 C 不等于会用 C 写业务系统,但读得懂 C,才读得懂上面这些系统的源码。

二、从机器语言到高级语言

层次 形式 优点 缺点 执行方式
机器语言 纯二进制指令 CPU 直接执行,最快 不可读、不可移植、难维护 直接运行
汇编语言 助记符(MOV、ADD) 可读性改善,与指令一一对应 仍依赖具体 CPU 架构 汇编器翻译后运行
高级语言 接近自然语言的语句 可读、可移植、开发快 需翻译,可能牺牲部分效率 编译或解释

三者的关键差别不在"难不难",而在抽象层次:机器语言描述"电信号怎么变",汇编描述"寄存器和内存怎么动",高级语言描述"要解决什么问题"。C 处在高级语言的下沿——它有 if、for、函数这些高级结构,又保留了指针和地址运算,可以直接操作内存。所以人们常说 C 是"高级语言中的低级语言",这个定位决定了它的全部优缺点。

按翻译方式,高级语言又分两类:编译型(C、C++、Go)先把整个源程序翻译成目标机器码再执行,运行时不依赖翻译器;解释型(Python、JavaScript)边翻译边执行,灵活但通常更慢。C 是典型的编译型语言。

三、开发工具怎么选

写 C 只需要三样东西:编辑器(写代码)、编译器(翻译)、调试器(找 bug)。把它们打包在一起就叫 IDE。常见选择:

工具 体积 上手难度 适合
Dev-C++ 小(几十 MB) 很低,新建源文件即可编译运行 课堂入门、单文件练习
Code::Blocks 小 低 教学、轻量工程
Visual Studio 数 GB 中,需要建"项目" 大型工程、Windows 开发
VS Code + MinGW-w64 中 中,需配编译器路径 长期使用、跨平台

选择判据很简单:如果目标是"今天就把第一个程序跑起来",选 Dev-C++;如果打算长期写代码,选 VS Code + MinGW-w64 或 Visual Studio。初学者最常见的坑是直接上 Visual Studio 新建 C++ 项目,结果被预编译头、项目属性、解决方案配置挡在门外——不是工具不好,是它面向的场景太重。

Linux 与 macOS 自带或易于安装 GCC/Clang,命令行两行就够:

gcc hello.c -o hello
./hello

第一条把源文件编译链接成可执行文件 hello,第二条运行它。-o 指定输出文件名,省略时 Windows 下默认生成 a.exe。

四、编译到底做了什么

一条 gcc 命令背后是四个阶段,理解它们能省下大量调试时间:

  1. 预处理:处理以 # 开头的行——把 #include 的头文件内容原地展开、替换 #define 的宏、删掉注释。产物是纯 C 代码(.i)。
  2. 编译:把 C 代码翻译成汇编代码(.s),同时做语法检查。语法错误在这一步报出。
  3. 汇编:把汇编翻译成机器指令,生成目标文件(.o / .obj)。此时函数调用还没有确定地址。
  4. 链接:把目标文件与库文件合并,解析函数地址,生成可执行文件。undefined reference 这类错误在这一步报出。

这条链路解释了两类报错的分工:expected ';' 是编译期的语法问题,undefined reference to 'printf' 是链接期找不到实现——通常是忘了包含头文件或忘了链接库。

五、第一个程序逐行拆解

#include <stdio.h>

int main(void)
{
    printf("Hello, World!\n");
    return 0;
}
  • #include <stdio.h>:预处理指令,把标准输入输出头文件的内容展开进来。printf 的声明在这个文件里,没有它编译器不认识 printf。
  • int main(void):主函数,整个程序唯一的入口,操作系统从这里开始执行。int 表示它返回一个整数给操作系统;void 表示不接收命令行参数。
  • { }:函数体的边界,里面是语句序列。
  • printf("Hello, World!\n");:调用标准库函数输出字符串。\n 是转义字符,表示换行;它属于字符串内容,不会被打印成两个字符。语句末尾的分号是语句结束符,不是分隔符。
  • return 0;:结束 main 并把 0 返回给操作系统。按惯例 0 表示正常结束,非 0 表示异常。

三条格式上的硬规则:C 区分大小写(Main 与 main 是两个标识符);所有语句以英文分号结尾;字符串必须用英文双引号。中文输入法下的全角引号、全角分号、全角括号,肉眼几乎看不出差别,编译器却会报 stray '\357' in program 之类莫名其妙的错误——遇到看不懂的报错,先检查标点是不是中文的。

易错点

  1. 把 main 拼成 mian,链接时报 undefined reference to 'WinMain' 或找不到入口。
  2. 用 void main()。这不是标准写法,C99 起要求 int main,部分编译器会警告甚至拒绝。
  3. 认为 C 是解释型语言。C 源码必须先整体编译成机器码才能运行。
  4. 漏写分号却盯着报错那一行看。编译器报的行号常常是下一行,因为它是读到下一行的内容才发现上一句没结束。
  5. 把 .c 文件改成 .cpp 后用 C++ 编译器编译。多数情况能过,但 C++ 的类型检查更严(如 void* 不能隐式转换),会引入莫名其妙的错误。

这一章在整门课里的位置

本章是整门课的入口,不依赖任何前置知识,但定下了后面所有章节的共同前提:程序要经过编译链接才能运行,语法错误与链接错误分属不同阶段。第 2 章开始讲数据类型,那是"内存里怎么存"的第一层抽象;第 3 章讲算法,是"怎么组织步骤";第 4 到 6 章的顺序、选择、循环三种结构,全部建立在 main 函数与语句序列这个概念之上。

本文是《C 语言程序设计》第 1 章的知识点摘要。完整板书与代码见 课程页;下一章 数据类型。

相关课程

这篇笔记涉及的主题,我录过对应的视频课程: