初识C语言
了解C语言的诞生与发展、标准演进、编程的七个步骤、编译链接流程——建立编程全局观
学习目标
- 能用自己的话画出编程的七个步骤,并解释编译和链接在整条流水线中分别处在哪个位置、各自干了什么
- 能说出本书覆盖的主要阶段(从 K&R 到 C11),并解释标准、实现定义行为与可移植性的关系
- 能回答:预处理、编译、汇编、链接四道工位各产出一个什么后缀的文件?如果 gcc 报
undefined reference,问题出在哪道工位?
为什么你敲的代码,电脑能看懂?
你打开编辑器、敲了几行代码、点了运行,屏幕上就出现了一行字。这中间其实经过了一条工厂流水线——你的代码是「原料」,经过好几道工位一道道加工,最后从流水线另一头出来一个能直接运行的「成品」。
这条流水线有四道工位:第一道把原料里杂七杂八的标注清理干净、把折叠好的部分摊开;第二道把人类能读的文字翻译成机器的"方言";第三道把方言逐字译成二进制编码;第四道把分散的零件拼成一个完整的、能直接跑的程序。除此之外,你还要知道怎么规划一个程序——就像造房子之前要先画图纸,编程也一样,不是坐下来就敲键盘。
这一章就带你走一遍这条流水线的四道工位,顺便看看「编程」这件事到底是怎么从"我想让电脑帮我算个数"变成一段能跑通的成品。没有这条流水线会怎样?你写的代码永远只是一堆人类能读的文字,电脑一个字也不认识。
C 语言从哪来?
在你动手写第一条 C 代码之前,先花几分钟看看这门语言是怎么来的——它对理解 C 语言"长什么样"很有帮助。
为什么使用 C(Why C)
C 用较小的语言核心提供结构化控制流、函数、指针和直接的数据表示,编译器容易生成高效机器码;标准化接口又让同一份源码可以面向不同目标重新编译。代价是内存边界、资源释放和许多平台差异需要程序员自己负责,因此“接近硬件的控制力”必须与清晰接口、诊断和测试一起使用。
从贝尔实验室到全世界
↡1972 年由 Dennis Ritchie 在贝尔实验室创造的一门通用编程语言。它以简洁、高效、可移植著称,是操作系统(Unix/Linux)、嵌入式系统、游戏引擎等领域的基石语言。 是 Dennis Ritchie 于 1972 年在贝尔实验室创造的。最初是为了重写 Unix 操作系统——在此之前,Unix 是用汇编写的,换个机器就要全部重来。Ritchie 想要一门「能跨机器运行的高级语言」,于是 C 诞生了。
C 语言的发展和标准化走了很长的路。下面是它的演进时间线:
C 语言的关键节点:
- 1972:Ritchie 在贝尔实验室创造了 C,用来重写 Unix
- 1978:Brian Kernighan 和 Dennis Ritchie 合著的《The C Programming Language》出版,定义了一个事实上的标准——人称「K&R C」
- 1989:ANSI 发布第一个正式 C 标准 C89,为语法、约束和库接口建立共同基线
- 1999:C99 标准发布,新增了
//单行注释、变长数组等特性 - 2011:C11 标准发布,引入多线程支持、泛型选择器等
C 语言在整个编程流程里的位置是起点——你用它写下的每一行代码,都是那条工厂流水线的「原料」。
编译器:把人话翻译成机器话
写了 C 代码,电脑怎么认识它?答案是靠一个叫 ↡把一种编程语言写的高级代码翻译成机器能执行的二进制指令的工具。C 语言常用的编译器有 GCC、Clang、MSVC 等。 的翻译官。你写的 C 代码是人类能读的文字,编译器做的事就是把它们翻译成机器能执行的二进制指令。
编译器在整个流程里,对应的是工厂流水线的第二道和第三道工位(编译+汇编)。它先检查你的语法有没有写错(括号对上了吗?分号没忘吧?),然后把 C 语句翻译成汇编语言,最后转成机器码。
编译与链接:四道工位
你现在知道了「写完代码到什么结果」中间有四个工位。下面把它们挨个摊开讲清楚。
↡编译器把源码翻译成机器码的过程。对于 C 语言,通常分为四步:预处理(展开头文件和宏)、编译(C→汇编)、汇编(汇编→二进制机器码)、链接(拼接多个目标文件和库成可执行文件)。 就是把你的 .c 文件一步步变成能运行的程序。而 ↡编译器工作的最后一道工位。把你编译出来的 .o 目标文件,和你用到的库里别人已经写好的机器码,拼在一起,变成一个能双击就跑的可执行文件。 是最后一步——把分散的「零件」(.o 文件)拼在一起,对接所有符号引用,变成可以跑的可执行文件。下面这张图把这四步一清二楚地画出来了:
每一步的解释:
-
第一道工位·预处理:预处理器做的都是「文本级」的工作。看见
#include <stdio.h>?把整个 stdio.h 文件的内容原样粘贴到这行;看见注释?删掉;看见#define?把用到的宏全部替换成对应的文字。处理完后生成了后缀名为.i的「纯净 C 代码」。 -
第二道工位·编译:编译器拿到
.i文件,把 C 语言的每一个语句翻译成汇编语言——一种贴近机器、但仍然用人类能读的助记符写的"方言"。如果语法有错(忘记分号、括号不配对、类型不匹配),就在这一步报错停下。输出.s文件。 -
第三道工位·汇编:汇编器把
.s文件里的每一条汇编助记符一对一翻译成二进制机器码,生成.o目标文件——这是「零件」,还不是能跑的东西。 -
第四道工位·链接:链接器把你的
.o文件和你用到的标准库(比如printf的实现就住在库文件里)拼在一起。所有对printf的引用都找到对应的地址,组装成一个能双击就跑的可执行文件。
这四步是常见编译器驱动展示的概念模型:预处理→编译→汇编→链接。具体实现可以合并阶段或不落盘生成 .i、.s 文件,但诊断仍可按预处理、翻译和链接边界理解。
编程机制(Programming Mechanics)
源文件、目标代码文件、可执行文件和库承担不同角色。.c 保存源码;目标文件包含面向某个目标平台的机器码与待解析符号;库归档可复用目标代码;链接器把所需定义组合成当前平台可装载的程序。GCC、Clang 或 IDE 通常作为驱动统一调用这些工具,用户不必手动运行每一阶段。
可移植性:一次编写,到处跑
C 语言最厉害的特性之一:写完一份代码,换个操作系统、换台电脑,只要用那个平台的编译器重新编译,就能生成在那个平台上能跑的程序。这就是 ↡写在标准 C 语言里的一份源码,用不同的编译器重新编译,就能在不同操作系统和 CPU 架构上生成行为一致的可执行文件——不需要为每个平台重写代码。。
可移植性以 C 标准为共同基线,但还要区分标准明确定义、实现定义、未指定和未定义行为。避免平台专有扩展只是第一步;还要留意整数宽度、字符集、字节序、文件系统和库实现差异。下面这张图展示“同一源码在不同目标上重新编译”的核心思路:
同一份 hello.c 可以分别由不同平台的实现重新编译。若程序只依赖标准定义的行为和目标平台都提供的能力,观察结果应符合相同程序契约;生成的可执行文件本身通常不能跨操作系统或 CPU 直接搬运。
动手:一步步走完编程的全过程
现在你知道了 C 语言怎么来、代码怎么变程序。下面跟着这条七步流水线,一步步看「编程」这件事到底从哪开始、到哪结束——每切一步,上面的循环图都会高亮当前这一步,下方说明这一步具体干什么。
猜一猜:七个步骤里哪一步最费时间?是敲代码、调试 bug、还是改需求?动手切一切,想想你过去的经历再说答案。
① 定义目标:想清楚程序要干什么
这是编程的起点——不是打开编辑器,而是拿支笔把「我想让程序干什么」写清楚。是算一个数学题?是展示一张图片?是管理一个通讯录?目标越具体越好。如果你跳过这一步直接开始敲代码,写到一半发现方向偏了——推倒重来。这一步决定了后面六步的方向。
你的第一个 C 程序
不废话了,先看代码——这是 C 语言里最经典的一小段:
#include <stdio.h>
int main(void) {
printf("Hello, World!\n");
return 0;
}每一行在说什么?#include <stdio.h> 把标准输入输出头文件的内容粘贴进来,让你能使用 printf。int main(void) 是整个程序的入口——操作系统运行你的程序时,第一件事就是钻进 main。printf("Hello, World!\n"); 往屏幕上输出一行文字,\n 是换行。return 0; 告诉操作系统「一切正常,我跑完了」。
编译和运行它,一条命令就够了:
# Linux / macOS
gcc hello.c -o hello
./hello
# 输出:Hello, World!在 Windows 上,Visual Studio 里按 Ctrl+F5 运行,Dev-C++ 里点编译运行按钮。背后做的事完全一样——预处理、编译、汇编、链接。
容易踩的坑
小结
- C 语言从 1972 年诞生,第 6 版主线覆盖 K&R C、C89、C99 与 C11
- 编程有七个步骤——定义目标、设计、编写、编译、运行、测试、维护——它们是一个反复迭代的循环
- 编译过程分四道工位:预处理(.i)→ 编译(.s)→ 汇编(.o)→ 链接(可执行文件),
gcc一条命令全包 - C 标准提供可移植基线,但实现定义和未定义行为仍要求程序显式处理平台边界
int main(void)是程序入口,printf通过<stdio.h>声明、在链接阶段从标准库拿到实现
练习
问题 1(改代码型) 把 Hello World 改成两行输出,并从终端运行。解释 \n 的作用,以及为什么业务代码不应只为防止窗口关闭而强行等待输入。
问题 2(问答型) 编程七个步骤里,哪一步你最容易想跳过?跳过它之后最可能导致什么问题?
问题 3(问答型) 编译器分别报了以下三种错误信息,各出在哪道工位?fatal error: 'stdoi.h' file not found、error: expected ';' before 'return'、undefined reference to 'print'。
名词解释
本章出现的专业名词,用大白话再讲一遍。
- C 语言
1972 年由 Dennis Ritchie 在贝尔实验室创造的一门通用编程语言。它以简洁、高效、可移植著称,是操作系统(Unix/Linux)、嵌入式系统、游戏引擎等领域的基石语言。详见本章「C 语言从哪来」一节。
- 编译器
把一种编程语言写的高级代码翻译成机器能执行的二进制指令的工具。C 语言常用的编译器有 GCC(Linux/macOS 默认)、Clang(macOS 默认)、MSVC(Visual Studio 自带)。详见本章「编译器」一节。
- 编译过程
编译器把源码翻译成机器码的过程。对于 C 语言,通常分为四步:预处理(展开头文件和宏 → .i)、编译(C→汇编 → .s)、汇编(汇编→二进制机器码 → .o)、链接(拼接多个 .o 和库成可执行文件)。详见本章「编译与链接」一节。
- 链接
编译器工作的最后一道工位。把你编译出来的 .o 目标文件,和你用到的库里别人已经写好的机器码,拼在一起,变成一个能双击就跑的可执行文件。详见本章「编译与链接」一节。
- 可移植性
源码在不同符合标准的实现上重新编译并保持程序契约的能力。标准提供共同规则,但程序仍需处理实现定义行为、平台接口和数据表示差异。