简介C入门、核心与提高讲义笔记面向刚开始学习C的编程新手也适合已有C语言基础、希望系统梳理面向对象与泛型编程思路的读者用于解决语法零散、知识体系不连贯的问题。资源包共1个PDF文件大小约5.07MB以讲义形式整合知识点便于按章节阅读和复习。内容从Visual Studio创建项目、编写并运行第一个C程序讲起串联注释、变量、常量、关键字、标识符命名规则及整型、浮点型、字符型等数据类型与sizeof用法并延伸至指针、数组、结构体、类、运算符重载、模板和异常处理等核心与提高内容。已有25997人学习适合作为课堂配套笔记、自学提纲或查漏补缺的速查材料帮助读者建立从入门语法到进阶编程技巧的完整学习路径。1. 从一行报红的 cout 说起这份 C 讲义笔记补的是哪块带过一个刚转方向的同学装完环境敲下cout Hello world整行报红改了半天以为是编译器的问题。翻回去看他手上根本没有一份能按顺序读下去的 C 编程语言讲义——东拼西凑看博客第 3 篇讲指针第 5 篇讲模板基础那块永远是空的。这份笔记按「入门 → 核心 → 提高」三段铺开入门篇把第一个程序拆成创建项目、创建文件、编写代码、运行程序四步紧跟着讲注释、变量、常量、关键字、命名规则核心篇落到数据类型整型家族、sizeof、字符与 ASCII、转义字符、字符串、bool、cin 输入一个不落运算符一章把算术、赋值、比较、逻辑四类一次讲完。它适合零基础照着抄一遍的人也适合写过别的语言、回来补 C 内存细节的人尤其是被隐式转换和类型大小坑过的那批。2. 讲义骨架拆解入门、核心、提高三段是怎么串成一条依赖链的2.1 三章内容的职责划分与前置依赖这份笔记的目录只有三块主干1 C初识、2 数据类型、3 运算符。表面看是平铺的三个大标题实际是一条硬依赖链跳着读必然断档。第 1 章解决的是「代码能跑起来」第 2 章解决的是「变量在内存里到底占多大、长什么样」第 3 章解决的是「这些变量之间怎么算」。没有第 1 章的编译链路第 2 章的sizeof打印不出来没有第 2 章的类型概念第 3 章的10 / 3为什么等于 3 就永远只能死记。把每章下面的小节摊开看职责划分得很清楚章节小节解决的落地问题前置依赖1 C初识1.1 第一个程序工程怎么建、代码怎么写、怎么跑无1 C初识1.2 注释代码可读性编译期被忽略1.11 C初识1.3 变量给内存起名类型 名字 初值1.11 C初识1.4 常量#define与const两条路1.31 C初识1.5 关键字哪些词不能拿来当名字1.3、1.41 C初识1.6 命名规则标识符的合法字符集1.52 数据类型2.1 整型short/int/long/long long1 章全部2 数据类型2.2 sizeof用运算符量出字节数2.12 数据类型2.3 实型float/double与有效数字2.22 数据类型2.4 字符型char与 ASCII 编码2.22 数据类型2.5 转义字符\n\t\\与八/十六进制转义2.42 数据类型2.6 字符串型C 风格数组与string2.4、2.52 数据类型2.7 布尔类型bool占 1 字节真为 1 假为 02.12 数据类型2.8 数据输入cin 读键盘2 章全部3 运算符3.1 算术运算符 - * / % --2 章全部3 运算符3.2 赋值运算符 - * / %3.13 运算符3.3 比较运算符 ! 返回 0/13.13 运算符3.4 逻辑运算符! ||与真假翻转3.3看这张表就知道哪几节最容易被人跳过2.2 的sizeof和 2.5 的转义字符。很多人觉得「字节数查手册就行」结果第一次跨平台传数据就被long在 Windows 上 4 字节、Linux 64 位上 8 字节打脸这种坑查手册是查不出来的必须自己用sizeof打印一遍。2.2 讲义类资源该按什么粒度拆文件这类讲义笔记资源拿到手第一件事不是从第一页读到最后一页而是先决定文件粒度。我一般按「一章一个 Markdown 一个 code 目录」的方式重组理由是读的时候按章翻改的时候按节改代码单独存一份可以直接编译不用从 Markdown 里复制粘贴再删掉注释符号。重组后的目录大概长这样cpp-notes/ ├── README.md # 阅读顺序与章节依赖图 ├── ch01-intro/ │ ├── 01-first-program.md │ ├── 02-comment.md │ ├── 03-variable.md │ ├── 04-const.md │ ├── 05-keyword.md │ └── 06-naming.md ├── ch02-datatype/ │ ├── 01-integer.md │ ├── 02-sizeof.md │ ├── 03-float.md │ ├── 04-char.md │ ├── 05-escape.md │ ├── 06-string.md │ ├── 07-bool.md │ └── 08-cin.md ├── ch03-operator/ │ ├── 01-arithmetic.md │ ├── 02-assign.md │ ├── 03-compare.md │ └── 04-logic.md └── code/ ├── ch02-01-integer.cpp ├── ch02-04-char.cpp └── ch03-01-arithmetic.cpp文件名建议带上章节号ch02-04-char.cpp这种命名在编辑器里排序就是阅读顺序比test1.cpp、新建文件夹2.cpp强太多。Markdown 里贴代码的地方除了保留完整片段还应该反手在code/目录里放一份可编译版本——讲义里的片段经常是半截的缺main函数、缺头文件直接抄会报一堆错有个能跑的原文件对照心态会稳很多。2.3 环境准备把讲义里的示例变成能跑的东西笔记里默认用 Windows 上那套集成开发环境建工程四步走打开 IDE、右键源文件选「添加 → 新建项」、给.cpp文件起名、写代码后点运行。图形界面的好处是新手不用碰命令行坏处是换台机器或者换到别的系统就抓瞎。我习惯同时准备一条命令行路线用g直接编译理解「源码 → 可执行文件」这个链路到底发生了什么。# 编译单个源文件-std 指定语言标准-Wall 打开全部常用警告-o 指定输出名 g -stdc11 -Wall code/ch02-04-char.cpp -o char_demo # 运行产物Windows 下是 char_demo.exeLinux 下就是 char_demo ./char_demo三个参数值得说清楚-stdc11决定能不能用auto、范围 for 这些新特性讲义里如果没写标准默认值可能不一样行为就会对不上-Wall是把警告打开很多人写float f 3.14编译不报错但结果有偏差开了-Wall才会有提示这个开关救过我很多次-o不写的话默认输出a.out文件名毫无辨识度几个示例编下来根本分不清哪个是哪个。提示命令行编译报g: command not found是编译器没装或者没进PATH跟代码没关系别对着源码改半天。3. 入门篇落地第一个程序、变量常量与命名规则3.1 四步走和 Hello World 的逐行拆解讲义把写出第一个程序分成四步创建项目、创建文件、编写代码、运行程序。前三步是工具操作真正决定你能不能入门的是第四步——能不能解释清楚每一行在干什么。把这段经典代码抄下来逐行读#includeiostream // 引入输入输出流库cout/endl 都来自这里 using namespace std; // 使用标准命名空间省掉每处的 std:: 前缀 int main() { // 程序入口操作系统从这里开始执行 cout Hello world endl; // 输出字符串并换行 system(pause); // 暂停等用户按键Windows 专用 return 0; // 返回 0 表示正常结束 }#includeiostream是预处理指令在编译之前由预处理器把iostream这个头文件的内容整个贴进来所以cout才认得using namespace std;把std命名空间里的名字全部暴露到当前作用域写cout等价于写std::cout小 Demo 里图省事没问题工程里我一般还是老老实实加std::避免和自定义的count、distance撞名。int main()的返回值类型必须是intreturn 0表示程序正常退出非 0 值通常代表异常。cout ... endl是链式调用在这里是重载过的输出运算符把右边的值送进左边的流里endl做两件事换行 刷新缓冲区。system(pause)是这一节最大的历史包袱。它调用的是系统命令行的pause程序只在 Windows 的控制台里存在。在现代 IDE 里按 CtrlF5 运行程序结束时会自动暂停这行完全多余换到 Linux 或者 macOS 上编译直接报错或者什么都没发生。提示如果只是想解决「窗口一闪而过」用cin.get()更通用不需要引入cstdlib也不依赖平台。3.2 变量必须初始化以及常量的两种写法变量的语法讲义写得很干脆数据类型 变量名 初始值;。注意「必须给初始值」这个说法严格讲 C 允许只声明不初始化比如int a;是合法的但此时a里存的是那块内存上的残留数据打印出来是随机值——这就是所谓的未定义行为。讲义的表述偏保守是为了让你养成初始化的习惯这个习惯值得保留代码里我基本没见过因为多写一个 0而出问题的场景。#includeiostream using namespace std; #define DAY 7 // 宏常量预处理器在编译前做文本替换 int main() { int a 10; // 定义并初始化a 里存 10 cout a a endl; // DAY 8; // 取消注释会报错宏常量不能被赋值 const int MONTH 12; // const 修饰的常量有类型、有作用域 cout 一年有 MONTH 个月份 endl; // MONTH 24; // 取消注释会报错const 变量只读 cout 一周有 DAY 天 endl; return 0; }#define DAY 7是预处理阶段完成的编译器看到DAY之前它已经被替换成字面量7换句话说编译器根本不知道有过DAY这个名字——所以调试器里看不到它报错信息里也只显示7。const int MONTH 12;是真正的变量只是被标记为只读享受类型检查、作用域规则、调试信息sizeof(MONTH)也是合法的。两者怎么选给个我自己的判断标准能用const就别用#define。宏的文本替换特性会带来一堆玄学问题比如#define SQUARE(x) x*x然后传SQUARE(a1)展开成a1*a1结果错得莫名其妙。现代 C 里这类常量还能写成constexpr编译期求值比const更强一层讲义里没提但值得知道。3.3 注释、关键字与命名规则这三块看着最没用实际最容易背锅注释这一节很短但两种形式必须分清//管到行尾/* */管到闭合符号为止且不能嵌套。编译器在预处理阶段会把注释替换成一个空格所以注释写多少都不影响运行效率。#includeiostream using namespace std; // 单行注释说明下面这行在做什么 int studentAge 20; // 也可以放在语句末尾 /* 多行注释通常放在一段逻辑的上方 这里演示变量的定义和输出 */ int main() { int score 95; cout age studentAge , score score endl; return 0; }关键字那一节把整张表都列了出来从asm、auto一路排到while、wchar_t大概六七十个。新人第一反应是「这怎么记得住」其实不用背写的时候撞上编译器会报错改个名就行。真正需要留个心眼的是那些「看起来很像普通单词」的new、delete、class、template、this、friend、operator、public、private写业务代码时习惯性起个class、new当变量名报错信息会让人愣半天。命名规则四条讲义总结得很清楚不能是关键字只能由字母、数字、下划线组成首字符必须是字母或下划线区分大小写。这四条之外实际项目里还有一层约定俗成的规范比如变量用小驼峰studentAge、常量全大写MAX_SIZE、类型名用大驼峰StudentInfo编译器不管这些但团队 review 时会管。int student_age 20; // 合法下划线分隔 int _count 0; // 合法下划线开头但不建议容易和标准库内部名字冲突 // int 2ndPlace 1; // 非法数字开头 // int student-age 1; // 非法出现减号 // int class 1; // 非法class 是关键字 int Class 1; // 合法大小写不同不是同一个标识符最后这个Class值得多说一句class是关键字不能当名字Class首字母大写就不是关键字了编译器放行。能编过不代表该这么写这种「只差一个大小写」的命名自己回头看代码时特别容易看串行血泪经验是跟关键字撞车的名字换个词别靠大小写绕。4. 数据类型与运算符把 sizeof、ASCII 和递增递减吃透4.1 整型家族与 sizeof先量字节再谈取值范围C 不会让你凭空定义一个变量int a;里的int就是在告诉编译器「给我留几个字节、按什么规则解释这块内存」。整型这一节列了四种short、int、long、long long区别只在占用的空间和能表示的范围。类型常见字节数取值范围备注short2-2^15 ~ 2^15-1-32768 ~ 32767短整型int4-2^31 ~ 2^31-1最常用longWindows 4 / Linux 64 位 8随平台变化跨平台慎用long long8-2^63 ~ 2^63-1大整数表里的「常见字节数」是有意这么写的因为标准只规定了「short不大于intint不大于longlong不大于long long」具体多少由编译器和平台决定。想知道自己机器上的真实值别背表用sizeof直接量#includeiostream using namespace std; int main() { // sizeof 是运算符不是函数括号里可以是类型名也可以是变量 cout short : sizeof(short) 字节 endl; cout int : sizeof(int) 字节 endl; cout long : sizeof(long) 字节 endl; cout long long : sizeof(long long) 字节 endl; int num 100; cout 变量 num : sizeof(num) 字节 endl; // 等价于 sizeof(int) cout 表达式 : sizeof(num 1) 字节 endl; // 不计算表达式只看类型 return 0; }sizeof有几个特性值得记牢它在编译期就能算出结果不产生运行时代码括号里放表达式时不会真的执行那个表达式只看它的类型返回类型是size_t无符号整数所以sizeof(int) - 5这种写法在 4 字节机器上会得到一个巨大的正数而不是 -1比较大小的时候要留神。讲义的结论是short int long long long注意中间那个小于等于不是严格小于。32 位平台上的long和int都是 4 字节这个「等于」就是跨平台坑的源头在本地把long当成 int 存数据传到 64 位 Linux 上它变成 8 字节结构体大小、文件偏移全对不上。我现在的习惯是凡是要写进文件或者走网络的整数一律用cstdint里的int32_t、int64_t长度写死在类型名里不靠平台。4.2 浮点、字符、字符串与输入精度和引号是最常翻车的地方浮点型分float和doublefloat占 4 字节、大约 7 位有效数字double占 8 字节、15 到 16 位有效数字。内存小一半精度差一半怎么选看场景图形学里成千上万个顶点用float省内存金融计算必须double差一位小数都是事故。#includeiostream using namespace std; int main() { float f1 3.14f; // 末尾的 f 不能省否则字面量是 double double d1 3.14; // 默认就是 double不用加后缀 cout f1 / d1 endl; cout float 占 sizeof(f1) 字节 endl; cout double 占 sizeof(d1) 字节 endl; float f2 3e2; // 科学计数法3 * 10^2 300 float f3 3e-2; // 3 * 10^-2 0.03 cout f2 / f3 endl; return 0; }3.14f那个f是新手最容易漏的。不写的话字面量类型是double赋给float会发生窄化转换MSVC 会甩一个 C4305 警告g 加-Wall也会提示但很多人开着默认配置就直接跑了。更坑的是精度float f 3.14;打印出来可能是3.1400001因为二进制没法精确表示 3.14double转float又丢了一次精度。字符型这一节的考点全在引号上。char ch a;用单引号且单引号里只能有一个字符双引号a是字符串末尾还带一个隐藏的\0类型是const char[2]赋给char直接编译报错。#includeiostream using namespace std; int main() { char ch a; cout ch endl; // 输出 a cout sizeof(ch) endl; // 1 字节 cout (int)ch endl; // 97强制转换成 ASCII 码看 ch 98; // 直接用编码赋值等价于 b cout ch endl; // ch abcde; // 错单引号里只能有一个字符 // ch a; // 错双引号是字符串不是 char return 0; }(int)ch这个强制转换是理解字符类型的关键char存的是编码不是字形a在内存里就是数字 97打印时按字符格式解释才显示成 a。ASCII 表 0 到 31 是不可打印的控制字符32 到 126 是键盘上能找到的字符A是 65、a是 97中间差 32所以大小写转换可以写ch 32但更稳妥的写法是用cctype里的tolower。转义字符这一节列了十几个实际写代码常用的就那么几个\n换行、\t水平制表符跳到下一个制表位、\\表示反斜杠本身、\在字符串里表示双引号。还有两个特殊写法要知道\ddd是三位八进制、\xhh是十六进制比如\101和\x41都表示A。#includeiostream using namespace std; int main() { cout \\ endl; // 输出一个反斜杠 cout \tHello endl; // 前面空一个制表位 cout line1\nline2 endl; cout \x41 / \101 endl; // 都是 A return 0; }字符串有两种风格C 风格的字符数组和 C 风格的string。前者用char str[] hello world本质是一个以\0结尾的数组后者用string str hello world得先#includestring好处是能直接赋值、拼接、比长度不用操心越界和\0。新项目里没有理由选前者除非要和 C 接口对接。#includeiostream #includestring // string 类型必须引这个头文件 using namespace std; int main() { char str1[] hello world; // C 风格数组长度 1211 个字符 \0 string str2 hello world; // C 风格长度 11 cout str1 | str2 endl; cout str2.size() endl; // 11string 有成员函数 return 0; }数据输入这一节只有一行cin 变量但行为边界特别多。cin读到空白字符空格、制表、换行就停所以cin str输入hello world只能拿到hello剩下的world留在缓冲区里下一次读会直接读到它。这不是 bug是设计如此。想读一整行带空格的用getline(cin, str)。另外cin 变量会做类型转换给int输入字母流会进入失败状态后续所有读取都失效得cin.clear()加cin.ignore()才能恢复这套组合拳在实际项目里出现频率极高。4.3 四类运算符与递增递减的求值顺序算术运算符这一节的重点不是 - *而是/和%。两个整数相除结果还是整数小数部分直接截断不是四舍五入10 / 3得 310 / 20得 0。想要小数结果至少让其中一个操作数是浮点10.0 / 3得 3.333...。取模%要求两边都是整数10 % 3得 1浮点数用%直接编译报错。除数和取模的右操作数都不能是 0运行期会崩这个检查得自己做。#includeiostream using namespace std; int main() { int a 10, b 3; cout a / b endl; // 3整数除法截断 cout a % b endl; // 1 double d1 0.5, d2 0.25; cout d1 / d2 endl; // 2浮点除法 // cout a / 0 endl; // 运行期崩溃除数为 0 // cout d1 % d2 endl; // 编译报错浮点不能取模 return 0; }递增递减的前置后置区别讲义用了一个很典型的对比b a * 10和b a * 10。前置是先加再用后置是先用再加。a 10时第一种a变成 11b得 110第二种b得 100a之后变成 11。这个规则本身不难难的是同一行里出现多个同一个变量比如a a a这类代码在 C11 之后属于未定义行为不同编译器结果可以完全不同看到就该改。#includeiostream using namespace std; int main() { int a2 10; int b2 a2 * 10; // 先 再乘a211, b2110 cout b2 b2 , a2 a2 endl; int a3 10; int b3 a3 * 10; // 先乘后 b3100, a311 cout b3 b3 , a3 a3 endl; return 0; }赋值运算符那六个复合写法 - * / %本质是语法糖a 2等价于a a 2但前者只求值一次左操作数对有副作用的下标表达式来说效率更高也更安全。比较运算符 ! 的返回值在 C 和 C 里是整数 1 和 0不是布尔值所以cout (10 20)打印出来是 1。逻辑运算符! ||有三条规则非运算真变假假变真与运算同真才为真其余为假或运算同假才为假其余为真。它的实际返回值同样是 1 和 0不是true/false的字面量。和||还有短路特性这个讲义没展开但特别实用a ! 0 10 / a 1里如果a是 0第一个条件为假右边的除法根本不会执行这就是用短路做防御性判断的写法。反过来如果写成10 / a 1 a ! 0a为 0 时直接崩顺序不能反。5. 踩坑排查这份讲义里五个最容易翻车的地方5.1 编译期就报错的cout、单双引号、关键字重名现象一cout、endl报「未声明的标识符」。原因有两个层次。第一层是没写#includeiostream编译器压根没见过cout的声明第二层是头文件写了但没写using namespace std;cout藏在std命名空间里直接写全名std::cout也能过。 解决先看头文件再看命名空间两样都补齐。新手最容易在抄代码时把using namespace std;这一行漏掉因为很多片段为了简洁会省略它贴到自己的文件里就报错。现象二char ch a;编译失败提示无法从const char[2]转换到char。原因是双引号构造的是字符串字面量类型是字符数组末尾还自动补\0长度为 2。char只能装一个字节的一种编码。 解决单字符一律用单引号a双引号留给字符串。反过来char str[] abc;也是错的多字符常量在不同编译器下行为不一致别试。现象三变量名报「语法错误标识符」。原因是名字撞了关键字比如int class 1;、int new 2;、double template 3;。这些词在 C 里有固定含义不能作标识符。 解决换个名字别靠大小写硬绕。int Class 1;能编过但六个月后回头看代码会怀疑人生。5.2 编过了但结果不对的整除、float 精度、cin 截断现象四写平均分计算(80 90 85) / 3得到 85 而不是 85.0或者干脆变成 85.00 但小数全是 0。原因是三个整数相加还是整数除数是整数整个表达式按整数除法走小数部分被截断之后再赋给double也补不回来。 解决把任意一个操作数变成浮点比如/ 3.0或者写static_castdouble(sum) / 3。注意顺序sum / 3先算完再转double是无效的。现象五float f 3.14;打印出来是3.1400001。原因有两层字面量3.14本身是double类型十进制 3.14 转成二进制是无限循环小数double存一个近似值赋给float时再截断一次精度又掉一截。 解决字面量加f后缀写成3.14f要比较浮点是否相等别用算两者差值的绝对值小于一个容差比如1e-6再判断。现象六cin str输入一个带空格的句子只读到了第一个单词。原因是的语义是「读取到下一个空白字符为止」空格、制表、换行都算分隔符。这不是错误是标准行为。 解决读整行用getline(cin, str)。如果前面刚用过cin 缓冲区里会残留一个换行符getline会直接读到空串需要先cin.ignore()把残留清掉。这个组合我见过太多次几乎每个做命令行交互的项目都会踩一遍。5.3 平台相关的long 的大小、system(pause) 和编码现象七本地算好的结构体序列化到文件换台 64 位 Linux 机器读出来全乱了。原因是结构体里用了longWindows 上 4 字节、Linux 64 位上 8 字节字段偏移全变。sizeof(long)在两个平台上打印出来的值就不一样更别说整体布局。 解决跨平台数据传输一律用定长类型int32_t/int64_t或者干脆序列化成文本/JSON不直接 dump 内存。我现在的规矩是只要这段内存要出门就不用long。现象八代码在自己电脑上跑得好好的拷到别的机器上一闪而过或者编译报system未声明。原因是system(pause)是 Windows 控制台专有命令在 Linux 上system需要#includecstdlib而且pause这个命令根本不存在会返回一个错误码然后继续往下跑。 解决删掉它。真的需要等待输入用cin.get()在 IDE 里跑程序直接按「开始执行不调试」的组合键IDE 本身就会停住。现象九中文字符串输出乱码。原因是源文件编码、编译器解释编码、控制台显示编码三者不一致。GBK 保存的源文件用 UTF-8 解释中文就成了问号或者方块。 解决源文件统一存成 UTF-8编译时加-finput-charsetUTF-8 -fexec-charsetGBKWindows 控制台之类的参数或者直接在代码里用英文输出排错先把逻辑跑通再处理显示问题。这个问题跟char占 1 字节也有关中文在 UTF-8 下占 3 字节用char存中文必然出问题得用wchar_t或者string配合正确的编码处理。6. 二次开发把讲义改成自己的题库和自动化验证脚本讲义读第二遍的时候我一般不再从头看而是把它改造成两样东西一份错题集一套能一键跑完全部示例的脚本。错题集的做法很土但有效每个.md文件末尾加一个「改错」小节把讲义里正确的示例故意改错三处比如把float f 3.14f改成3.14、把10 / 3改成10 / 3.0再问「结果变了吗、为什么」。第二天不看原文只看改错题能全部说清楚才算过。这个方法的成本几乎为零收益是把「看懂了」和「会用了」之间的那道坎填上了。自动化验证脚本解决的是另一个问题讲义里的几十个示例改完环境或者换台机器怎么确认全都还能编过、跑出来的结果还对我的做法是建一个run_all.sh遍历code/目录逐个编译执行把输出重定向到文件再和一份基准输出做 diff。#!/usr/bin/env bash # 遍历 code 目录下所有 cpp 文件逐个编译运行失败即停止 set -e mkdir -p build out for src in code/*.cpp; do name$(basename $src .cpp) echo 编译 $name g -stdc11 -Wall -Wextra $src -o build/$name echo 运行 $name ./build/$name out/$name.txt 21 done echo 全部通过set -e让脚本在任意一步失败时立刻退出不会带着错误继续往下跑-Wall -Wextra把警告开到最大很多类型转换的隐患在这一步就能暴露把 stdout 和 stderr 一起重定向到文件是因为有些示例会打印sizeof的结果这和平台绑定换机器 diff 会不一致得提前有个心理准备必要时把这些输出改成注释再生成基准。验证方法还有一条更狠的拿编译器当考官。把讲义里某一段代码的某个关键字删掉看编译器报什么错然后用自己的话把这个错的成因讲一遍。比如删掉#includestring之后string str;报什么、删掉using namespace std;之后cout报什么、把const int MONTH 12;的const去掉再赋值会怎样。这些错误信息看多了以后再遇到类似的报错定位速度会快一个数量级比背语法条文管用得多。从那以后我每读完一章都会把讲义里的示例代码删掉一半自己重写一遍再跑一次run_all.sh跑不过就不往下推进——这套动作多花的时间远比事后 debug 一段「看着眼熟其实没懂」的代码要少。希望帮到你。本文还有配套的精品资源点击获取