C++ 语言基础与程序运行 / C++ Language Basics and Program Execution
1. 这不是一张语法表
你写下:
cpp
int answer = 42;表面上,这只是声明一个整数变量。
但编译器和运行时需要连续回答很多问题:
text
answer 这个名字指向什么?
int 允许保存哪些值?
42 用什么二进制形式表示?
answer 的存储在哪里?
它什么时候开始成为一个对象?
它什么时候失效?
这行代码最后会变成什么机器指令?所以,学习 C++ 基础不是把 if、for、int、float 和指针分别背下来。
真正要建立的是一条连接:
text
源代码
|
v
编译器理解名字、类型和表达式
|
v
汇编器生成目标文件
|
v
链接器组合多个目标文件
|
v
操作系统装载可执行文件
|
v
进程中的对象、栈和控制流开始运行本模块的开头会先给出清楚的入口。
但“通俗”不等于只给一句字典式定义。
介绍一个概念时,我们还要继续追问:
text
它表示什么?
它怎样被实现?
它解决什么问题?
它不保证什么?
它和相邻概念有什么区别?
它会给程序带来什么后果?例如,int 不只是“整数类型”:
text
int
|
+-- 用来表示离散的整数值
+-- 常见实现使用固定宽度的二进制表示
+-- 表示范围受宽度限制
+-- 运算可能发生整数提升和类型转换
+-- 有符号溢出会进入危险的语言边界
+-- 类型选择会影响接口、内存和机器指令float 也不只是“浮点类型”:
text
float
|
+-- 用有限位数近似表示实数
+-- 通常包含符号、指数和有效数部分
+-- 大多数十进制小数不能被精确表示
+-- 运算会受到舍入和精度影响
+-- 还要考虑 NaN、无穷和比较误差这就是本模块的解释尺度:先让人进入,再把概念讲到可以推理。
2. 本模块要解决的五类问题
text
源码问题
+-- 名字明明写了,为什么编译器说找不到?
构建问题
+-- 每个 .cpp 都能编译,为什么链接仍然失败?
对象问题
+-- 变量离开作用域后,为什么原来的地址不能继续用?
接口问题
+-- 函数收到数组后,为什么不知道数组有多长?
运行问题
+-- 输入错误、越界和未初始化为什么不会都在编译期报错?这些问题共同指向:
text
名字 -> 类型 -> 对象 -> 存储 -> 生命周期 -> 访问 -> 错误3. 模块地图
text
C++ Language Basics
|
+-- A. 程序如何被翻译
| +-- 01-compile-link-odr.md
|
+-- B. 值、类型与对象
| +-- 02-language-fundamentals.md
|
+-- C. 表达式与控制流
| +-- 03-expressions-operators-control-flow.md
|
+-- D. 函数、作用域与调用
| +-- 04-functions-scope-overloading.md
|
+-- E. 数组、指针与字符串
| +-- 05-arrays-pointers-strings.md
|
+-- F. 引用、const 与类型推导
| +-- 06-references-const-type-deduction.md
|
+-- G. 结构化数据与内存表示
| +-- 07-enums-structs-unions-aliases.md
|
+-- H. 预处理器、头文件与命名空间
| +-- 08-preprocessor-headers-namespaces.md
|
+-- I. 输入、错误与调试
+-- 09-basic-io-errors-debugging.md这九篇不是九个互不相干的知识点。
它们按照依赖关系连接:
text
程序翻译
|
v
值、类型、对象
|
v
表达式和控制流
|
v
函数和作用域
|
v
数组、地址、指针
|
v
引用和类型推导
|
v
结构体和数据表示
|
v
头文件和多文件程序
|
v
输入、错误和调试4. C++ 同时生活在哪些层
同一行 C++ 代码,可能同时涉及多个层次。
text
+------------------------------------------------------+
| 你的 C++ 源码 |
+------------------------------------------------------+
|
v
+------------------------------------------------------+
| ISO C++:类型、对象、表达式、生命周期、标准库 |
+------------------------------------------------------+
|
v
+------------------------------------------------------+
| 编译器:语义检查、代码生成、优化 |
+------------------------------------------------------+
|
v
+------------------------------------------------------+
| ABI:调用约定、对象布局、名字、二进制兼容 |
+------------------------------------------------------+
|
v
+------------------------------------------------------+
| 操作系统:进程、虚拟地址空间、文件、线程、I/O |
+------------------------------------------------------+学习时必须区分结论来源。
例如:
| 问题 | 主要属于哪一层 |
|---|---|
int 能否转换为 float | ISO C++ |
int 占几个字节 | 实现和平台 |
| 函数参数放在哪些寄存器 | ABI 和目标架构 |
| 程序启动后拥有什么地址空间 | 操作系统 |
std::cin 如何报告输入失败 | C++ 标准库 |
gdb 如何显示调用栈 | 调试工具 |
以后看到“通常”“一般实现”“在 Linux 上”,要意识到这可能不是语言标准保证。
5. 源码怎样变成正在运行的程序
text
main.cpp / math.cpp
|
v
预处理:展开头文件、处理宏和条件编译
|
v
翻译单元
|
v
编译:检查语法、类型和语义,生成汇编或目标文件
|
v
汇编:把汇编文本变成机器码
|
v
目标文件:代码、数据、符号、重定位信息
|
v
链接:解决跨文件引用并安排地址
|
v
可执行文件
|
v
操作系统装载
|
v
进程、主线程、地址空间和运行时对象这个链路解释了三类常见错误:
text
编译错误
+-- 单个翻译单元无法被理解
链接错误
+-- 多个目标文件无法正确组合
运行时错误
+-- 程序已经生成,但实际路径违反了约束6. 计算机基础补习线
本模块不会要求先学完编译原理。
每次只补当前主题需要的那一小块知识。
text
学函数
-> 补调用栈、返回地址和参数传递
学数组和指针
-> 补连续内存、地址、对齐和越界
学头文件
-> 补预处理、翻译单元和符号
学结构体
-> 补字段偏移、padding 和对象表示
学输入
-> 补缓冲区、状态机和错误传播这样,计算机基础不是从 C++ 主线旁边单独开一门课,而是每次在需要时补上。
7. 文章分组导读
7.1 程序翻译组
文章:
text
01-compile-link-odr.md读完后应该能回答:
text
为什么声明可以帮助编译?
为什么定义仍然需要在链接时找到?
什么是目标文件、符号和重定位?
为什么 ODR 错误可能不在单个源文件里暴露?7.2 值、类型与对象组
文章:
text
02-language-fundamentals.md
03-expressions-operators-control-flow.md这组建立语言规则和运行结果之间的联系。
7.3 函数和内存组
文章:
text
04-functions-scope-overloading.md
05-arrays-pointers-strings.md
06-references-const-type-deduction.md这组把表达式带到调用栈、地址、借用和生命周期。
7.4 数据表示和程序组织组
文章:
text
07-enums-structs-unions-aliases.md
08-preprocessor-headers-namespaces.md这组解释对象怎样布局,以及多个源文件怎样合作。
7.5 输入、错误与调试组
文章:
text
09-basic-io-errors-debugging.md这组把前面知识放进一个真正会失败的程序。
8. 贯穿练习:文本统计器
text
读取一行文本
|
v
解析整数和单词
|
v
用函数拆分行为
|
v
用数组、指针和字符串保存数据
|
v
用引用和 const 表达借用与修改
|
v
用枚举和结构体表达解析状态
|
v
拆成头文件与源文件
|
v
处理错误输入并使用调试器每篇文章只增加一个主要能力,并故意制造一个对应错误。
目标不是做出产品,而是让每个抽象都能被观察。
9. 每篇文章都要回答的问题
text
这个概念解决什么问题?
它的最小例子是什么?
它的正式定义是什么?
编译器如何处理它?
运行时如何表现?
它的限制是什么?
错误在哪个阶段出现?
如何用实验验证?
它和下一篇有什么关系?10. 两条阅读路线
完整路线:
text
00 -> 01 -> 02 -> 03 -> 04 -> 05 -> 06 -> 07 -> 08 -> 09快速路线:
text
00 -> 01 -> 02 -> 04 -> 05 -> 0911. 模块完成标准
完成后,你应该能够:
- 解释 C++ 源码如何成为可执行程序。
- 区分编译错误、链接错误和运行时错误。
- 解释
int、float等类型的用途、表示、范围和限制。 - 区分名字、类型、对象、存储和生命周期。
- 画出一个简单数组和指针的关系。
- 解释函数参数、局部变量和调用栈。
- 说明引用、指针和借用视图的差异。
- 解释结构体 padding 的来源。
- 把程序拆成头文件和源文件。
- 处理基本输入失败。
- 写实验验证自己的判断。
如果还不能做到,不要急着背智能指针。
智能指针表达的是所有权。
要理解所有权,必须先理解对象、地址和生命周期。