NOI C++ 课程体系 / 计算机基础 / 第3讲
CSP-J 入门级 · 计算机基础 · m1-03

程序设计语言 与编译运行基础

从机器语言到高级语言,从源代码到可执行文件 —— 理解程序是如何被"翻译"并运行的。

6 大模块 3 代语言进化 2 种翻译方式 4 步编译流程 12 道分层练习
本讲 · 第 3/56 讲程序设计语言与编译运行基础
模块 01

情景导航

用类比和故事建立对"编程语言"和"编译"的直觉理解。

故事联合国翻译官的启示

想象你是一位中国外交官,要在联合国大会上发表演讲。你用中文(高级语言)写好了演讲稿,但各国代表只听得懂自己的母语(机器语言)。

于是你需要一位翻译官(编译器)把你的中文逐句翻译成各国语言,代表们才能理解并执行。

程序员写的是"中文"(源代码),CPU 只懂"外语"(机器码)!

编译器就是这个翻译官 —— 它把你写的 C++ 代码翻译成 CPU 能直接执行的二进制指令(0和1)。

💡 两种翻译方式

现实中,翻译有两种方式:

① 同声翻译(解释型):你说一句,翻译官立刻翻译一句。说完就完了,没有留下翻译稿。

② 提前翻译(编译型):翻译官提前把整篇稿子翻译好,打印出来发给代表。代表们拿着翻译稿自己看,不需要翻译官在场。

C++ 属于"编译型",Python 属于"解释型"!

①生活类比:编程语言的三代进化
🔢

机器语言 = 摩尔斯电码

全是0和1,只有机器能读懂,人根本无法阅读。

→ 第一代语言
📝

汇编语言 = 简写暗号

用 MOV、ADD 等缩写代替数字,稍好理解但仍很底层。

→ 第二代语言
💬

高级语言 = 自然语言

接近人类语言,if/for/while 一看就懂。

→ 第三代语言
🤖

AI语言 = 说人话

直接用自然语言描述需求,AI自动生成代码。

→ 第四/五代语言
②编程定义

程序设计语言:人与计算机之间交流的工具,用于描述解决问题的步骤(算法)。

编译:将高级语言源代码一次性翻译成机器语言(目标程序)的过程。

解释:将高级语言源代码逐句翻译并立即执行,不生成目标程序。

📌 本讲核心知识地图

板块核心内容对应大纲
语言发展机器语言→汇编→高级语言→第四/五代程序设计语言基本概念
语言分类低级 vs 高级、编译型 vs 解释型程序设计语言分类
编译与解释编译器 vs 解释器,各自优缺点程序编译和运行的基本概念
C++编译流程预处理→编译→汇编→链接,四步详解编译运行全流程
③核心口诀
🔑 语言三代

一代机器0和1,二代汇编用缩写;
三代高级近人语,C/C++/Java 都属于;
越高级越好写,越低级越快跑!

🔑 编译 vs 解释

编译一次全翻译,生成exe跑得快;
解释逐句边翻边跑,不生成目标程序;
C/C++ 走编译,Python/JS 走解释!

🔑 C++编译四步

预处理 → 展开头文件和宏
编译 → 源码变汇编代码(.s)
汇编 → 汇编变目标文件(.o)
链接 → 合并所有.o生成exe!

模块 02

知识探究

深入了解程序设计语言的发展、分类以及编译运行的核心机制。

1程序设计语言发展史

程序设计语言经历了从"机器能懂"到"人能好写"的进化过程:

第一代:机器语言(1940s)
由 0 和 1 组成的二进制指令,CPU 直接执行。
例:10110000 01100001(把97送入寄存器)
优点:执行速度最快;缺点:难写、难读、易出错。
第二代:汇编语言(1950s)
用英文缩写(助记符)代替二进制指令。
例:MOV AL, 61h(同上功能)
需要汇编器翻译成机器码。仍与硬件密切相关。
第三代:高级语言(1960s~至今)
接近自然语言和数学公式,不依赖特定硬件。
代表:C(1972)、C++(1983)、Java(1995)、Python(1991)
需要编译器或解释器翻译。
第四/五代:非过程化语言(1980s~至今)
更接近自然语言,描述"做什么"而非"怎么做"。
代表:SQL(数据库查询)、Prolog(逻辑推理)、Lisp
AI时代:ChatGPT/Copilot 直接用自然语言生成代码。
2语言的分级:低级 vs 高级
级别名称代表语言特点翻译方式
第一代机器语言二进制指令CPU直接执行,不可移植无需翻译
第二代汇编语言x86 ASM助记符,与硬件相关汇编器
第三代高级语言C/C++/Java/Python接近自然语言,可移植编译器/解释器
第四代非过程化语言SQL/Prolog描述"做什么"专用解释器
第五代智能语言Lisp/自然语言AI辅助生成AI模型
🔑 低级 vs 高级

低级语言(机器/汇编):贴近硬件,执行效率高,但难写、难移植。
高级语言(C/C++/Java):贴近人类思维,好写好读,可跨平台。
越高级 → 越好写但翻译开销越大;越低级 → 越快但越难写。

🔧 低级语言
  • 机器语言:01010101...
  • 汇编语言:MOV AX, 5
  • 与特定CPU架构绑定
  • 执行效率最高
  • 操作系统内核、驱动程序用
💬 高级语言
  • C/C++:编译型,速度快
  • Java:半编译半解释
  • Python:解释型,开发快
  • 可在不同平台上运行
  • 应用程序、竞赛编程用
3编译型 vs 解释型:两种翻译方式

高级语言编写的程序(源代码),CPU 不能直接执行,必须翻译成机器语言。翻译方式有两种:

📝
源代码
hello.cpp
→
⚙️
编译器
一次性翻译
→
📦
目标程序
hello.exe
→
▶️
直接运行
不需要编译器
📝
源代码
hello.py
→
🔄
解释器
逐句翻译
→
▶️
边翻译边执行
需要解释器在场
对比项编译型(C/C++)解释型(Python/JS)
翻译时机运行前一次性全部翻译运行时逐句翻译
目标程序生成 .exe / .o 文件不生成目标程序
运行速度快(直接执行机器码)慢(每次都要翻译)
跨平台需针对不同平台重新编译只要有解释器就能运行
调试编译时报错,运行时无翻译开销运行时逐句报错
代表语言C、C++、Rust、GoPython、JavaScript、Ruby
✅ 编译型优势
  • 运行速度快(竞赛必选!)
  • 编译后不需要编译器
  • 可优化生成高效机器码
  • 代码可加密(不暴露源码)
✅ 解释型优势
  • 开发速度快(改完即运行)
  • 跨平台方便
  • 动态类型、灵活
  • 适合脚本、快速原型

💡 Java 的特殊性

Java 是"半编译半解释":先编译成字节码(.class),再由 JVM(Java虚拟机)解释执行。这让它既有编译的效率,又有解释的跨平台性。

4C++ 程序的编译运行全流程

一个 C++ 程序从编写到运行,要经历四个阶段:

📝 源文件
hello.cpp
→ ① 预处理 → 📄 展开后源码 → ② 编译 → 🔧 目标文件
hello.o
🔧 hello.o + 📚 库文件
libc++.a
→ ③ 链接 → ▶️ 可执行文件
hello.exe
阶段工具输入输出做什么
① 预处理cpp.cpp展开后源码展开 #include、处理 #define 宏
② 编译g++ -c展开后源码.o(目标文件)将源码翻译成机器码
③ 链接ld.o + 库文件.exe(可执行)合并目标文件,解析外部函数
④ 运行./hello.exe输出结果操作系统加载并执行
5编译四步详解
① 预处理(Preprocessing)
  • 处理所有 # 开头的指令
  • #include:把头文件内容复制粘贴进来
  • #define:宏替换(文本替换)
  • #ifdef/#endif:条件编译
  • 删除注释
  • 命令:g++ -E hello.cpp
② 编译(Compilation)
  • 语法检查:变量声明、括号匹配
  • 语义检查:类型匹配、函数调用
  • 生成汇编代码(.s 文件)
  • 再汇编成目标文件(.o / .obj)
  • 每个 .cpp 独立编译
  • 命令:g++ -c hello.cpp
③ 链接(Linking)
  • 合并所有 .o 文件
  • 链接标准库(如 iostream 的实现)
  • 解析函数调用地址
  • 生成最终可执行文件
  • "undefined reference" 错误就是链接失败
  • 命令:g++ hello.o -o hello
④ 运行(Execution)
  • 操作系统加载 .exe 到内存
  • CPU 从 main() 开始执行
  • 加载动态链接库(.dll/.so)
  • 程序输出结果
  • 返回退出码(0=正常)
  • 命令:./hello
案例案例1:Hello World 编译全过程

让我们追踪一个最简单的 C++ 程序从编写到运行的全过程:

C++hello.cpp 源代码
#include <iostream>
using namespace std;

int main() {
    cout << "Hello, World!" << endl;
    return 0;
}
终端编译运行命令
# 方式一:一步到位(最常用)
g++ hello.cpp -o hello
./hello

# 方式二:分步执行(学习用)
g++ -E hello.cpp -o hello.ii    # ① 预处理
g++ -S hello.cpp -o hello.s     # ② 生成汇编
g++ -c hello.cpp -o hello.o     # ③ 编译为目标文件
g++ hello.o -o hello             # ④ 链接生成可执行
./hello                             # ⑤ 运行
运行结果
Hello, World!

📁 各阶段产生的文件

文件扩展名说明大小(约)
hello.cpp.cpp源代码(人写的)~100B
hello.ii.ii预处理后(展开了头文件)~30KB
hello.s.s汇编代码(人类可读的机器指令)~3KB
hello.o.o目标文件(二进制机器码)~5KB
hello.exe(Windows)可执行文件~50KB

💡 注意:.ii 文件有30KB是因为 #include <iostream> 展开了大量头文件内容!

模块 03

实践应用

通过分析和判断题,巩固对编程语言分类和编译过程的理解。

题1语言分类判断

题目:以下语言分别属于哪种翻译方式?请将它们分类。

C, Python, Java, C++, JavaScript, Rust, Ruby

查看参考答案
类型语言说明
编译型C, C++, Rust直接编译为机器码,运行速度快
解释型Python, JavaScript, Ruby由解释器逐句执行
半编译半解释Java先编译为字节码(.class),再由JVM解释执行
题2编译过程分析

题目:以下编译命令分别对应编译流程的哪个阶段?

① g++ -E main.cpp
② g++ -c main.cpp -o main.o
③ g++ main.o utils.o -o program
④ ./program

查看参考答案

① 预处理:-E 选项,只预处理不编译,输出展开后的源码。

② 编译:-c 选项,编译但不链接,生成目标文件 .o。

③ 链接:将多个 .o 文件链接为可执行文件。

④ 运行:执行生成的可执行文件。

题3错误定位分析

题目:以下三种错误分别发生在编译的哪个阶段?

A. "undefined reference to `foo()'"(未定义的引用)
B. "expected ';' before '}' "(缺少分号)
C. 程序运行时崩溃(段错误 Segmentation Fault)

查看参考答案

A → 链接阶段错误:编译器找到了 foo() 的调用,但找不到它的定义(实现)。说明链接器无法将函数调用与函数体连接起来。

B → 编译阶段错误:语法错误,编译器在语法分析时发现缺少分号。

C → 运行阶段错误:程序已成功编译和链接,但运行时访问了非法内存地址(如数组越界、空指针)。

规律:编译时发现语法错误,链接时发现引用错误,运行时才暴露逻辑错误!

模块 04

总结提升

1核心要点总结
知识点必须记住的核心
语言三代机器语言(0/1) → 汇编(助记符) → 高级语言(近自然语言)
低级 vs 高级低级贴近硬件快但难写;高级贴近人类好写但需翻译
编译型一次性翻译,生成目标程序(.exe),运行快。代表:C/C++
解释型逐句翻译,不生成目标程序,开发快。代表:Python/JS
编译四步预处理 → 编译 → 汇编 → 链接
预处理展开 #include,处理 #define 宏,删除注释
编译语法/语义检查,生成 .o 目标文件
链接合并 .o + 库文件,生成 .exe 可执行文件
g++命令g++ hello.cpp -o hello(一步完成编译链接)
2拓展:编译优化

💡 g++ 编译优化选项

-O0:不优化(默认),编译最快,适合调试

-O1:基本优化,减少代码大小和运行时间

-O2:中等优化(竞赛推荐),在-O1基础上增加更多优化

-O3:最大优化,编译慢但运行最快

-std=c++17:指定C++标准版本

竞赛推荐命令:g++ -O2 -std=c++17 hello.cpp -o hello

3思考与启发

💡 学完本讲,你应该明白的5个道理

1. 程序设计语言分低级和高级,C++ 属于第三代高级语言。

2. 编译型(C++)一次性翻译生成exe,运行快;解释型(Python)逐句翻译,开发快。

3. C++ 编译经历预处理→编译→链接三步(或细分为四步),每步产生不同文件。

4. 语法错误在编译时发现,引用错误在链接时发现,逻辑错误在运行时才暴露。

5. 竞赛中使用 g++ -O2 -std=c++17 编译,可获得更好的运行效率。

模块 05

学习检测

5 个初赛高频易错点。

易错1

高级语言不需要翻译

❌ 错误
"高级语言写的程序,计算机可以直接运行。"
✅ 正确
高级语言必须翻译成机器语言才能执行。翻译方式要么编译,要么解释。
口诀:CPU只认0和1,高级语言必须翻译!
易错2

编译和解释是一回事

❌ 错误
"编译就是解释,解释就是编译。"
✅ 正确
编译是运行前一次性全部翻译,生成目标程序;解释是运行时逐句翻译,不生成目标程序。
口诀:编译一次搞定,解释逐句来!
易错3

汇编语言是高级语言

❌ 错误
"汇编语言属于高级语言。"
✅ 正确
汇编语言是第二代低级语言,与硬件密切相关。C/C++ 才是第三代高级语言。
口诀:汇编是二代低级,C/C++才是三代高级!
易错4

编译后的程序不需要操作系统

❌ 错误
"编译成exe就不需要操作系统了。"
✅ 正确
程序运行时仍需操作系统加载到内存、分配资源、调度CPU。操作系统是程序运行的基础。
口诀:exe也要操作系统来"伺候"!
易错5

Python 是编译型语言

❌ 错误
"Python 和 C++ 一样是编译型语言。"
✅ 正确
Python 是解释型语言,由 Python 解释器逐句翻译执行。不生成独立的 .exe 文件。
口诀:C++ 编译出 exe,Python 靠解释器跑!
速查程序设计语言与编译速查表
知识点关键结论
机器语言第一代,0和1,CPU直接执行
汇编语言第二代,助记符,需汇编器翻译
高级语言第三代,近自然语言,需编译或解释
编译型一次翻译生成exe,运行快(C/C++)
解释型逐句翻译不生成exe,开发快(Python)
预处理展开#include,处理#define宏
编译.cpp → .o,语法检查+生成机器码
链接.o + 库 → .exe,合并解析
g++ 命令g++ -O2 -std=c++17 main.cpp -o main
📌知识点笔记卡

📜 语言三代

一代:机器语言(0/1)

二代:汇编(助记符)

三代:高级语言(C/C++)

⚙️ 编译型

一次性翻译

生成目标程序(.exe)

运行快,C/C++属于

🔄 解释型

逐句翻译执行

不生成目标程序

Python/JS属于

🔧 预处理

展开#include

处理#define宏

删除注释

📦 编译

语法/语义检查

.cpp → .o

生成机器码

🔗 链接

.o + 库 → .exe

合并目标文件

解析外部函数

课后练习

分层练习作业

A基础 / B提高 / C挑战,难度递增,点击展开答案与解析。

🟢 A组 · 基础(4题)

A1选择题

以下属于低级语言的是()

A. Python   B. Java   C. 汇编语言   D. C++

查看参考答案

答案:C. 汇编语言

汇编语言是第二代低级语言。Python、Java、C++ 都是第三代高级语言。

考点:语言按“面向机器还是面向问题”划分层级:机器语言、汇编语言是低级语言。

易错:因为汇编语言有英文助记符、比机器码好读,就误判它是高级语言;也可能把 Python 当成“低级的脚本语言”。

A2选择题

关于编译型语言和解释型语言,下列说法正确的是()

A. 编译型语言运行速度通常比解释型快
B. 解释型语言会生成目标程序
C. 编译型语言每次运行都要重新翻译
D. C++ 是解释型语言

查看参考答案

答案:A

编译型语言运行前已翻译成机器码,运行时直接执行,所以速度快。解释型不生成目标程序,每次运行都要逐句翻译。C++ 是编译型。

考点:编译型语言先整体翻译成机器码再运行,解释型语言每次运行都要逐句翻译,因此编译型速度更快。

易错:把两者的特点对调,误以为“解释型运行更快”;或误以为 C++ 是解释型语言。

A3选择题

C++ 源程序文件的扩展名通常是()

A. .exe   B. .obj   C. .cpp   D. .o

查看参考答案

答案:C. .cpp

.cpp 是 C++ 源文件扩展名。.exe 是可执行文件,.obj/.o 是目标文件。

考点:认识各类文件扩展名:C++ 源文件 .cpp,目标文件 .o/.obj,可执行文件 .exe。

易错:把目标文件 .obj/.o 当成源文件;或答 .c——那是 C 语言的源文件扩展名。

A4选择题

编译预处理阶段主要处理的内容是()

A. 语法检查
B. 展开 #include 头文件和 #define 宏
C. 链接库文件
D. 生成可执行文件

查看参考答案

答案:B

预处理阶段处理所有以 # 开头的指令:展开 #include、替换 #define 宏、处理条件编译 #ifdef。

考点:预处理阶段只处理以 # 开头的预处理指令,如 #include 展开、#define 宏替换、#ifdef 条件编译。

易错:以为预处理会做语法检查或翻译成机器码;这两件事分别属于编译阶段,不属于预处理。

🟡 B组 · 提高(4题)

B1选择题

以下关于源程序和目标程序的说法,正确的是()

A. 目标程序可以在任何计算机上直接运行
B. 源程序经过编译后生成目标程序
C. 解释型程序也会生成目标程序
D. 目标程序就是源程序

查看参考答案

答案:B

源程序经编译后生成目标程序(.o/.obj)。A错(目标程序与平台相关),C错(解释型不生成目标程序),D错(两者完全不同)。

考点:源程序经编译生成目标程序,目标程序再经链接生成可执行文件,三个文件层层递进。

易错:以为目标程序与平台无关(其实它和具体机器、操作系统相关);或以为解释型语言也会生成目标程序。

B2选择题

编译过程中,"undefined reference to" 错误发生在哪个阶段?

A. 预处理   B. 编译   C. 链接   D. 运行

查看参考答案

答案:C. 链接

"undefined reference" 表示函数被调用了但找不到实现,这是链接器的工作范围。语法错误(如缺少分号)才发生在编译阶段。

考点:编译阶段只检查语法,链接阶段负责把“函数调用”与“函数实现(定义)”连接起来。

易错:看到英文报错就一律归结为编译阶段;也容易混淆 undefined reference(链接期,缺实现)与缺分号(编译期语法错)。

B3排序题

请将 C++ 编译流程的四个阶段按正确顺序排列:

链接、预处理、生成可执行文件、编译生成目标文件

查看参考答案

答案:

① 预处理(展开头文件/宏)

② 编译生成目标文件(.cpp → .o)

③ 链接(.o + 库文件)

④ 生成可执行文件(.exe)

思路:按“先处理 # 指令 → 把每个 .cpp 独立翻译成目标文件 → 再由链接器合并目标文件和库”的时间顺序排下来即可。

易错:把链接排到编译前面;或把“生成可执行文件”当成独立阶段,其实它只是链接的产物。

B4简答题

简述编译型语言和解释型语言的区别,并各举两个例子。

查看参考答案

编译型语言:在运行前,由编译器将全部源代码一次性翻译成机器语言(目标程序),生成可执行文件(.exe)。运行时直接执行机器码,不需要编译器在场。运行速度快。

例子:C、C++、Rust、Go

解释型语言:在运行时,由解释器逐句翻译源代码并立即执行,不生成独立的目标程序。每次运行都需要解释器参与。开发效率高但运行速度慢。

例子:Python、JavaScript、Ruby、PHP

思路:从三个维度对比即可:翻译时机(运行前一次性 / 运行时逐句)、是否生成目标程序、运行速度,再各举两个语言例子。

易错:只答“一个快一个慢”而不说清原因;或举例时把 C 写成解释型、把 Python 写成编译型。

🔴 C组 · 挑战(4题)

C1初赛真题

下列关于程序设计语言的叙述中,错误的是()

A. 机器语言是计算机唯一能直接识别和执行的语言
B. 高级语言编写的程序需要翻译成机器语言才能执行
C. 汇编语言是面向机器的高级语言
D. 高级语言与具体的机器指令系统无关

查看参考答案

答案:C

汇编语言是低级语言,不是高级语言!它是面向机器的第二代语言,与特定CPU架构密切相关。

考点:汇编语言是第二代、面向机器的低级语言,用助记符代替二进制机器指令,与具体 CPU 架构相关。

易错:只看“有助记符、可读性好”就认为它是高级语言;判断标准应是“面向机器还是面向问题”,而不是好不好读。

C2初赛真题

下列关于编译程序和解释程序的叙述中,正确的是()

A. 编译程序和解释程序都能产生目标程序
B. 编译程序不能产生目标程序,而解释程序能
C. 编译程序能产生目标程序,而解释程序不能
D. 编译程序和解释程序都不能产生目标程序

查看参考答案

答案:C

编译程序将全部源代码翻译成目标程序(.o/.exe);解释程序逐句翻译执行,不产生目标程序。

考点:编译程序与解释程序的根本区别在于翻译单位和产物:一个整体翻译并产出目标程序,一个逐句翻译且不产出目标程序。

易错:以为解释程序也会生成目标程序;或把“整体/逐句”这一翻译单位记反。

C3综合分析

一个 C++ 项目包含 main.cpp 和 utils.cpp 两个源文件。请写出完整的分步编译命令,并解释每一步的作用。

查看参考答案
# ① 编译 main.cpp 为目标文件
g++ -c main.cpp -o main.o

# ② 编译 utils.cpp 为目标文件
g++ -c utils.cpp -o utils.o

# ③ 链接两个目标文件为可执行程序
g++ main.o utils.o -o program

# ④ 运行
./program

解释:

• -c 选项表示"只编译不链接",生成 .o 目标文件

• 每个 .cpp 文件独立编译,互不干扰

• 最后由链接器将两个 .o 合并为一个可执行文件

• 这种分步编译的好处:修改一个文件时,只需重新编译那个文件,其他不变

思路:多文件项目要“先分后合”:每个 .cpp 用 g++ -c 单独编译成 .o,最后把所有 .o 一起链接成一个可执行文件。

易错:漏掉 -c 选项,直接对单个 .cpp 链接会因缺少另一个文件的符号而报错;也可能把两个 .cpp 一次性编译,没有体现分步编译的意义。

C4综合应用

某同学写了以下 C++ 代码并尝试编译,请分析每条错误信息属于编译的哪个阶段,并指出原因:

#include <iostream>
using namespace std;

int main() {
    int x = 10
    cout << calculate(x) << endl;
    return 0;
}

编译报错:① error: expected ';' before 'cout'
② 修复分号后报错:undefined reference to 'calculate(int)'

查看参考答案

错误 ① → 编译阶段

原因:int x = 10 后面缺少分号 ;。这是语法错误,编译器在语法分析时就能检测到。

错误 ② → 链接阶段

原因:代码调用了 calculate(x) 函数,但没有定义(实现)这个函数。语法检查通过(编译器知道这是一个合法的函数调用),但链接器找不到函数体的实现,所以报 "undefined reference" 错误。

修复方法:添加 calculate 函数的定义,或者在编译时链接包含该函数的目标文件/库。

思路:先看报错关键词判断阶段:缺分号是源代码语法问题(编译阶段),undefined reference 是找不到函数实现(链接阶段),再分别定位到具体语句或函数定义。

易错:把 undefined reference 也当成语法错误,跑去改函数调用的写法,而真正要做的是补上函数定义。