第一章 C++语言概述语言是怎么走到今天这样的,C++ 的特点和版本怎么选,一个程序由哪几块拼成,第一个程序从写下来到跑起来中间发生了什么。底子打牢,后面看到报错才知道往哪查。┌────────────────────────────────────────────────────────┐ │ 这一章要讲的事,一张图看完 │ ├────────────────────────────────────────────────────────┤ │ 第一章 C++ 语言概述 │ │ │ │ │ ├── 1. 计算机程序设计语言简介 三代语言怎么走到人这边 │ │ ├── 2. C++ 语言的特点和发展 强在哪,版本怎么选 │ │ ├── 3. C++ 程序的基本结构 一个程序由哪几块拼成 │ │ ├── 4. 编写第一个 C++ 程序 从源码到跑起来的四步 │ │ └── 5. 小结 一张表收口,加练习 │ └────────────────────────────────────────────────────────┘这一章没有难的东西,全是认门。认完门,后面每一章都是往里走。例子别光看,每个都自己敲一遍,敲过的代码才记得住。一、计算机程序设计语言简介1.1 程序是一份指令清单计算机这东西,本质上是个只会算数的傻子。你让它显示一个字,它内部要转过好几道弯。它听得懂的只有 0 和 1,别的都不会说。程序,就是我们写给它的指令清单。问题在于没人想用 01 写清单,看一行都费劲,改一晚上能要命。于是有了程序设计语言,充当人和机器之间的中间人。我们用接近人的写法描述意图,再让翻译程序把它变成 01。┌──────────────────────────────────────────────────────────┐ │ 程序设计语言的三级阶梯 │ ├──────────────────────────────────────────────────────────┤ │ 语言一代一代改,方向只有一个,离人越来越近 │ │ │ │ ┌────────────────────────────────────────────────────┐ │ │ │ 第一代 机器语言 01001000 00100001,全是 0 和 1 │ │ │ └────────────────────────────────────────────────────┘ │ │ │ │ │ v │ │ ┌────────────────────────────────────────────────────┐ │ │ │ 第二代 汇编语言 MOV AX, 1,给 01 起了名字 │ │ │ └────────────────────────────────────────────────────┘ │ │ │ │ │ v │ │ ┌────────────────────────────────────────────────────┐ │ │ │ 第三代 高级语言 cout << "Hi";,写着像句子 │ │ │ └────────────────────────────────────────────────────┘ │ │ │ │ 三代今天都还在,越靠下跑得越快,越靠上写起来越省事 │ └──────────────────────────────────────────────────────────┘三代语言今天都还在。汇编在驱动和引导代码里活着,高性能计算里也有人用。高级语言这一档,C++ 算离机器近的,这也是它快的原因之一。代际长相离谁近长处代价机器语言01001000机器跑得最快人读不了,改不动汇编语言MOV AX, 1机器贴着硬件,性能可控要记机器的脾气,换个平台重写高级语言cout << "Hi";人写得快,读得懂要经过翻译,损失一点性能1.2 编译型和解释型,一条分水岭高级语言机器看不懂,得有人翻译。翻译方式分两种,编译和解释,这一条决定了语言的很多脾气。┌─────────────────────────────────────────────────────────┐ │ 翻译的两种方式 │ ├─────────────────────────────────────────────────────────┤ │ 高级语言机器看不懂,得有人翻译,方式有两种 │ │ │ │ 编译型,C++ 走这条 解释型,Python 走这条 │ │ │ │ 源码 hello.cpp 源码 hello.py │ │ │ │ │ │ v v │ │ 编译器一次翻完 解释器一行行翻 │ │ │ │ │ │ v v │ │ 可执行文件 没有中间文件 │ │ │ │ │ │ v v │ │ 跑起来快,翻一次就够 改一行跑一行,方便 │ └─────────────────────────────────────────────────────────┘编译型解释型代表语言C、C++、Go、RustPython、JavaScript、Ruby怎么跑开工前整体翻成可执行文件跑的时候一行行翻快慢快慢一点,换来的是灵活改代码改完要重编改完直接跑排错编译期就拦下一大批错跑到哪错到哪C++ 是编译型。这句话的分量在后面会越来越明显。写之前要先编过,改完要重编,编译期能拦下一大批错,运行速度也有保障。编译慢的那几秒,换来运行时的快和写代码时的安全感,这笔账我觉得划算。当然这是我个人的看法,跑小脚本的时候我也用 Python,谁跟自己的时间过不去呢。二、C++ 语言的特点和发展2.1 出身,贝尔实验室的一间办公室1979 年,贝尔实验室,一个叫 Bjarne Stroustrup 的丹麦人想给 C 语言加个 class,让大型程序好写一点。项目先叫 C with classes,意思是带 class 的 C。他的名字国内教程常写成 本贾尼·斯特劳斯特卢普,太长了,后面我就用 本贾尼 这个简称。1983 年改名 C++。这个名字是同事提的,++ 来自 C 的自增运算符,读作 C plus plus,意思是比 C 进一步。这个自带吹嘘味的命名,后来成了行业经典案例。┌─────────────────────────────────────────────────────────────────────┐ │ C++ 的四十年 │ ├─────────────────────────────────────────────────────────────────────┤ │ 从 1979 到现在,一条线看完 │ │ │ │ 1979 贝尔实验室,Bjarne Stroustrup 动手,项目叫 C with classes │ │ │ │ │ 1983 改名 C++,++ 取自 C 的自增运算符,意思是比 C 进一步 │ │ │ │ │ 1985 第一份商业发行,同名的书也在这一年出来 │ │ │ │ │ 1998 第一版国际标准,C++98,模板和 STL 进场 │ │ │ │ │ 2003 小修小补,C++03,没有新语法 │ │ │ │ │ 2011 大改,auto、lambda、智能指针都来了 │ │ │ │ │ 2014 小步前进,C++14 │ │ │ │ │ 2017 继续补强,C++17 │ │ │ │ │ 2020 大版本,concepts、ranges、协程 │ │ │ │ │ 2023 目前最新的标准,工具链支持还在铺开 │ └─────────────────────────────────────────────────────────────────────┘2.2 标准版本,认准 C++11 之后的版本年份看点C++981998第一版国际标准,模板和 STL 进场C++032003修 bug,没有新语法C++112011auto、lambda、智能指针、范围 for,公认的分水岭C++142014泛型 lambda 之类的小补丁C++172017结构化绑定、optional、文件系统库C++202020concepts、ranges、协程C++232023目前最新,工具链支持还在铺开2011 是分水岭。2011 之前的老写法,和新写法放在一起,读起来像两门语言。业界把 2011 之后的叫 modern C++,本书全程按这个来。版本选择不用纠结。写新代码,标准直接上 C++17 或 C++20,g++ 编译时加-std=c++17就行。语法主体十几年没大变,把精力放在编程思想上,别耗在版本对比上。2.3 它强在哪特点说明新手能感受到什么快编译成机器码,零开销抽象是设计目标同样的算法,它常常跑在第一档面向过程也能面向对象class、继承、多态都有先当 C 用,慢慢再体会对象泛型模板让一套代码通吃多种类型STL 的容器和算法都靠它标准库全容器、算法、线程、文件都有现成的不用什么都自己造兼容 C绝大多数 C 代码能直接编老库拿过来就能用跨平台一份源码,各平台各编各的换系统要重编,不用重写拿它干活的领域,都是性能敏感或者离硬件近的地方。领域拿它干什么眼熟的项目游戏引擎与玩法逻辑Unreal Engine浏览器与运行时渲染引擎、JS 引擎Chrome 的 Blink 和 V8数据库存储引擎MySQL、MongoDB嵌入式与汽车固件、控制软件车载系统、机器人金融交易系统高频交易对延迟敏感大型桌面软件性能吃紧的客户端修图软件、办公套件的部分模块丑话说在前面。它不好学。语法细节多,历史包袱重,报错信息一长串,指针那一关能卡住一批人。学会的回报也摆在那儿,性能和就业面都真实存在。想清楚再上车,上车就认真开。2.4 和 C 的关系大体上,C++ 是 C 的超集,绝大多数 C 代码能直接当 C++ 编。少数地方行为不同,比如类型转换的规则,混写时要留意。学 C++ 之前不用先学 C,直接上。遇到看不懂的老式写法,多半是 C 时代留下来的,查一下就知道。三、C++ 程序的基本结构3.1 最小程序,五样东西#include <iostream> using namespace std; int main() { cout << "Hello, World!" << endl; return 0; }这七行是能跑起来的最小单位,拆开看,统共五样东西。┌──────────────────────────────────────────────────────────┐ │ 最小程序的骨架 │ ├──────────────────────────────────────────────────────────┤ │ 七行代码,统共五样东西 │ │ │ │ ┌────────────────────────────────────────────────────┐ │ │ │ #include <iostream> 预处理指令,请工具库进来 │ │ │ │ using namespace std; 标准库的名字可以直接写 │ │ │ │ int main() { 程序入口,系统只从这里开工│ │ │ │ cout << "Hi"; 语句,真正干活的 │ │ │ │ return 0; 告诉系统,正常收工 │ │ │ │ } 函数体到此为止 │ │ │ └────────────────────────────────────────────────────┘ │ │ │ │ 漏哪一样,编译器都会拦你,报错信息就是线索 │ └──────────────────────────────────────────────────────────┘成分长什么样干什么预处理指令#include把工具库的声明请进来命名空间声明using namespace std;标准库的名字可以直接写函数int main() { ... }干活的单元,程序入口也叫 main语句cout << "Hello, World!" << endl;一条命令,分号收尾返回值return 0;告诉操作系统干得怎么样逐个说。include 那行是预处理指令。iostream 是标准库的输入输出组件,cout、endl 都在里面住着。不请它,后面全抓瞎。using namespace std; 是命名空间声明。std 是标准库住的大院,不声明的话,写 cout 要写全名 std::cout。新手阶段先写上,能省一半打字量。等代码有规模了,再讨论要不要省。int main() 是程序入口。系统启动程序,只认 main 这个名字,从第一行花括号开始执行。一个程序只能有一个 main,写两个链接会报错。花括号划出函数体的范围,里面的语句按顺序一条条跑。分号是语句的句号,漏一个,编译器能给你报一串错,而且报错位置经常偏一行,这是 C++ 的老传统。return 0 告诉操作系统,活干完了,没出事,0 是正常收工的暗号。现在 main 里不写 return 也可以,编译器会默认加,写上是老规矩,图个清楚。3.2 注释,写给自己看的注释有两种写法,编译器看见就当没看见。// 单行注释,两个斜线,到此为止 /* 块注释 斜杠星开头,星斜杠结尾,中间随便写 */代码是写给机器的,注释是写给人的。三个月后回来看自己的代码,你会感谢当时写注释的那个人。四、编写第一个 C++ 程序4.1 挑一个工具工具适合谁备注g++想敲命令学的MinGW-w64,或 Linux、macOS 的包管理器Visual StudioWindows 上做项目社区版免费,安装时勾 C++ 工作负载VS Code + C/C++ 扩展轻量编辑器党装扩展,再配上 g++CLion要强力补全和调试收费,学生免费在线编译器只想跑一段代码wandbox、godbolt 之类本章例子用 g++,命令行三行跑完,不挑编辑器。装好之后先验货。C:\> g++ --version g++ (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 8.1.0Windows 上装完 MinGW,要把安装目录下的 bin 加进 PATH,命令行才认 g++。认的话万事大吉,不认的话,把路径加进去,重启命令行再试。4.2 从源码到跑起来,四步新建一个文件,取名 hello.cpp。后缀必须是 .cpp,这是 C++ 源码的招牌,.c 是给 C 的,编译器靠后缀决定按哪套规则来。#include <iostream> using namespace std; int main() { cout << "Hello, World!" << endl; return 0; }存好,编译,一条命令。g++ hello.cpp -o hello-o hello 告诉编译器,产物叫 hello。不写的话,Windows 下默认叫 a.exe,Linux 和 macOS 下默认叫 a.out,都能跑,名字不好记。这一条命令底下,流水线跑了四步。┌───────────────────────────────────────────────────────────┐ │ 编译链接走四步 │ ├───────────────────────────────────────────────────────────┤ │ 一条 g++ 命令,底下跑了四步 │ │ │ │ ┌────────────────────────────────────────────────────┐ │ │ │ 第一步 预处理 展开 include,把头文件拷进来 │ │ │ └────────────────────────────────────────────────────┘ │ │ │ │ │ v │ │ ┌────────────────────────────────────────────────────┐ │ │ │ 第二步 编译 翻成汇编代码 │ │ │ └────────────────────────────────────────────────────┘ │ │ │ │ │ v │ │ ┌────────────────────────────────────────────────────┐ │ │ │ 第三步 汇编 翻成机器码,装进目标文件 │ │ │ └────────────────────────────────────────────────────┘ │ │ │ │ │ v │ │ ┌────────────────────────────────────────────────────┐ │ │ │ 第四步 链接 和标准库拼成可执行文件 │ │ │ └────────────────────────────────────────────────────┘ │ │ │ │ 四步的产物依次是 hello.i、hello.s、hello.o、hello.exe │ └───────────────────────────────────────────────────────────┘阶段干什么产物预处理展开 include,把头文件拷进来hello.i编译翻成汇编代码hello.s汇编翻成机器码,装进目标文件hello.o链接和标准库拼成可执行文件hello / hello.exe想看中间产物,加参数单独跑,每一步都能停下来看。g++ -E hello.cpp -o hello.i # 只做预处理 g++ -S hello.cpp -o hello.s # 做到汇编 g++ -c hello.cpp -o hello.o # 做到目标文件排查离奇问题的时候,这几条能救命。模板那章的报错,一半靠它们看懂。4.3 跑起来┌──────────────────────────────────────────────────────────┐ │ 第一个程序从写到跑 │ ├──────────────────────────────────────────────────────────┤ │ 第一个程序,四步走完 │ │ │ │ ┌────────────────────────────────────────────────────┐ │ │ │ 写源码 新建 hello.cpp,把代码敲进去 │ │ │ └────────────────────────────────────────────────────┘ │ │ │ │ │ v │ │ ┌────────────────────────────────────────────────────┐ │ │ │ 编译 g++ hello.cpp -o hello,一次跑完 │ │ │ └────────────────────────────────────────────────────┘ │ │ │ │ │ v │ │ ┌────────────────────────────────────────────────────┐ │ │ │ 运行 Windows 输 hello,其他系统输 ./hello │ │ │ └────────────────────────────────────────────────────┘ │ │ │ │ │ v │ │ ┌────────────────────────────────────────────────────┐ │ │ │ 看结果 屏幕上出现 Hello, World! │ │ │ └────────────────────────────────────────────────────┘ │ └──────────────────────────────────────────────────────────┘Windows 下在命令行输入 hello,Linux 和 macOS 下输入 ./hello,屏幕上出现 Hello, World! 这一行,第一个程序就算成了。给自己鼓个掌,后面还有一百个程序要写。逐行拆一遍。代码作用#include把输入输出工具库请进来using namespace std;标准库的名字可以直接写int main() {程序入口,系统从这里开工cout << "Hello, World!" << endl;往屏幕上写一句话,endl 是换行加刷新return 0;告诉系统正常收工}函数体结束cout 读作 see out,是标准库的输出对象,可以把它当成屏幕的化身。<< 是往它里面送东西的通道,叫插入运算符。送字符串就显示字符串,送数字就显示数字,送 endl 就换个行。再写一个带输入的,试试 cin。#include <iostream> using namespace std; int main() { int a = 0, b = 0; cout << "请输入两个整数,用空格分开: "; cin >> a >> b; cout << a << " + " << b << " = " << a + b << endl; return 0; }跑起来是这样。请输入两个整数,用空格分开: 3 5 3 + 5 = 8cin 是输入通道,>> 把键盘敲的东西取出来,按变量的类型存好。a 和 b 声明成 int,敲 3 和 5 进去,加起来,原样送出来。这里有个细节,int 存不了小数,也存不下太大的数,超了会溢出。这是下一章的内容,先留个印象。4.4 输出中文乱码,Windows 专属坑程序里写中文,屏幕上出现一串问号或者怪符号,这个坑 Windows 用户躲不掉。┌──────────────────────────────────────────────────────────┐ │ 中文乱码是怎么发生的 │ ├──────────────────────────────────────────────────────────┤ │ 两边编码对不上,字就变了样 │ │ │ │ ┌────────────────────────────────────────────────────┐ │ │ │ 源码存成 UTF-8,字符串按 UTF-8 的字节存 │ │ │ └────────────────────────────────────────────────────┘ │ │ │ │ │ v │ │ ┌────────────────────────────────────────────────────┐ │ │ │ 程序把字节原样送到屏幕 │ │ │ └────────────────────────────────────────────────────┘ │ │ │ │ │ v │ │ ┌────────────────────────────────────────────────────┐ │ │ │ 命令行按 GBK 解码,代码页 936 │ │ │ └────────────────────────────────────────────────────┘ │ │ │ │ │ v │ │ ┌────────────────────────────────────────────────────┐ │ │ │ 对不上,屏幕出现乱码 │ │ │ └────────────────────────────────────────────────────┘ │ │ │ │ 解法三条,让两边用同一种编码 │ └──────────────────────────────────────────────────────────┘源码存成 UTF-8,字符串按 UTF-8 的字节存。程序运行时把字节原样送到屏幕。Windows 简体中文的命令行默认按 GBK 解码,代码页 936。两边对不上,乱码就来了。三条解法,挑一条。命令行切 UTF-8,执行 chcp 65001,再跑程序。切完最好重开窗口。源文件存成 GBK。VS Code 右下角点编码,Save with Encoding,选 GB2312 或 GBK。换 Windows Terminal,默认 UTF-8,一劳永逸,微软商店免费下。Linux 和 macOS 默认 UTF-8,没这个烦恼。4.5 新手常踩的坑现象原因解法syntax error 指到分号分号打成全角换回半角分号error: 'cout' was not declared漏了 include 或 using补上undefined reference to std::cout用 gcc 编 .cpp换 g++程序能编,中文乱码源码 UTF-8,命令行 GBK见 4.4 节error: 'mian' was not declaredmain 拼错改回 main提示不是内部或外部命令g++ 没进 PATH把 bin 目录加进 PATH报错看不懂的时候,把整段错误信息复制去搜。搜索引擎见过的新手坑比你想的多,这个习惯不高级,但是管用。五、小结5.1 本章知识收口表模块你现在应该能做对的事自测语言三代说清机器、汇编、高级的差别□ 能解释为什么高级语言要翻译编译与解释知道 C++ 是编译型以及它带来什么□ 能说出编译四阶段的名字C++ 版本知道 C++11 是分水岭□ 会加 -std=c++17程序结构认出五样成分□ 不看书能默写最小程序第一个程序独立写完编译跑通□ 屏幕上出现 Hello, World!排错常见坑知道往哪查□ 乱码和分号错能自己修┌─────────────────────────────────────────────────────┐ │ 一章走完该带走的东西 │ ├─────────────────────────────────────────────────────┤ │ 学完这一章,兜里该有这五样 │ │ │ │ +--------------+--------------------------------+ │ │ |模块 |带走什么 | │ │ +--------------+--------------------------------+ │ │ |语言三代 |机器、汇编、高级,一代比一代省事| │ │ |编译与解释 |C++ 是编译型,翻一次跑很久 | │ │ |版本 |认准 C++11 之后,加 -std=c++17 | │ │ |程序结构 |include、main、return 五样东西 | │ │ |第一个程序 |写、编、跑加排错 | │ │ +--------------+--------------------------------+ │ │ │ │ 哪一块心里没底,回去找对应小节,别急着往下翻 │ └─────────────────────────────────────────────────────┘5.2 五句话记住本章程序是写给机器的指令清单,语言是中间人,C++ 站在离机器近的那一档。C++ 是编译型,翻一次跑很久,改代码要重编。版本认准 C++11 之后,命令加 -std=c++17。最小程序五样东西,include、命名空间、main、语句、return。编译报错先看第一条,后面那一串常常是它引出来的。5.3 课后练习默写最小程序,不看书,编译跑通,打印 Hello, World!。改 4.3 节的程序,输入两个整数,输出它们的差、积、商,商保留两位小数。打印一份自我介绍,三行,姓名、城市、一句爱好,用 cout 分开写。故意制造三个错,全角分号、漏 include、main 拼错,把报错信息抄下来,对着 4.5 节的表找原因。执行 chcp 65001,跑一次带中文输出的程序,观察乱码消失的过程。附录|命令速查卡想干什么命令看编译器版本g++ --version编译链接一条龙g++ hello.cpp -o hello只做预处理g++ -E hello.cpp -o hello.i只做到汇编g++ -S hello.cpp -o hello.s只做到目标文件g++ -c hello.cpp -o hello.o指定 C++ 标准g++ -std=c++17 hello.cpp -o hello跑起来Windows 输 hello,Linux 和 macOS 输 ./hello命令行切 UTF-8chcp 65001
第一章 C++语言概述语言是怎么走到今天这样的,C++ 的特点和版本怎么选,一个程序由哪几块拼成,第一个程序从写下来到跑起来中间发生了什么。底子打牢,后面看到报错才知道往哪查。┌────────────────────────────────────────────────────────┐ │ 这一章要讲的事,一张图看完 │ ├────────────────────────────────────────────────────────┤ │ 第一章 C++ 语言概述 │ │ │ │ │ ├── 1. 计算机程序设计语言简介 三代语言怎么走到人这边 │ │ ├── 2. C++ 语言的特点和发展 强在哪,版本怎么选 │ │ ├── 3. C++ 程序的基本结构 一个程序由哪几块拼成 │ │ ├── 4. 编写第一个 C++ 程序 从源码到跑起来的四步 │ │ └── 5. 小结 一张表收口,加练习 │ └────────────────────────────────────────────────────────┘这一章没有难的东西,全是认门。认完门,后面每一章都是往里走。例子别光看,每个都自己敲一遍,敲过的代码才记得住。一、计算机程序设计语言简介1.1 程序是一份指令清单计算机这东西,本质上是个只会算数的傻子。你让它显示一个字,它内部要转过好几道弯。它听得懂的只有 0 和 1,别的都不会说。程序,就是我们写给它的指令清单。问题在于没人想用 01 写清单,看一行都费劲,改一晚上能要命。于是有了程序设计语言,充当人和机器之间的中间人。我们用接近人的写法描述意图,再让翻译程序把它变成 01。┌──────────────────────────────────────────────────────────┐ │ 程序设计语言的三级阶梯 │ ├──────────────────────────────────────────────────────────┤ │ 语言一代一代改,方向只有一个,离人越来越近 │ │ │ │ ┌────────────────────────────────────────────────────┐ │ │ │ 第一代 机器语言 01001000 00100001,全是 0 和 1 │ │ │ └────────────────────────────────────────────────────┘ │ │ │ │ │ v │ │ ┌────────────────────────────────────────────────────┐ │ │ │ 第二代 汇编语言 MOV AX, 1,给 01 起了名字 │ │ │ └────────────────────────────────────────────────────┘ │ │ │ │ │ v │ │ ┌────────────────────────────────────────────────────┐ │ │ │ 第三代 高级语言 cout << "Hi";,写着像句子 │ │ │ └────────────────────────────────────────────────────┘ │ │ │ │ 三代今天都还在,越靠下跑得越快,越靠上写起来越省事 │ └──────────────────────────────────────────────────────────┘三代语言今天都还在。汇编在驱动和引导代码里活着,高性能计算里也有人用。高级语言这一档,C++ 算离机器近的,这也是它快的原因之一。代际长相离谁近长处代价机器语言01001000机器跑得最快人读不了,改不动汇编语言MOV AX, 1机器贴着硬件,性能可控要记机器的脾气,换个平台重写高级语言cout << "Hi";人写得快,读得懂要经过翻译,损失一点性能1.2 编译型和解释型,一条分水岭高级语言机器看不懂,得有人翻译。翻译方式分两种,编译和解释,这一条决定了语言的很多脾气。┌─────────────────────────────────────────────────────────┐ │ 翻译的两种方式 │ ├─────────────────────────────────────────────────────────┤ │ 高级语言机器看不懂,得有人翻译,方式有两种 │ │ │ │ 编译型,C++ 走这条 解释型,Python 走这条 │ │ │ │ 源码 hello.cpp 源码 hello.py │ │ │ │ │ │ v v │ │ 编译器一次翻完 解释器一行行翻 │ │ │ │ │ │ v v │ │ 可执行文件 没有中间文件 │ │ │ │ │ │ v v │ │ 跑起来快,翻一次就够 改一行跑一行,方便 │ └─────────────────────────────────────────────────────────┘编译型解释型代表语言C、C++、Go、RustPython、JavaScript、Ruby怎么跑开工前整体翻成可执行文件跑的时候一行行翻快慢快慢一点,换来的是灵活改代码改完要重编改完直接跑排错编译期就拦下一大批错跑到哪错到哪C++ 是编译型。这句话的分量在后面会越来越明显。写之前要先编过,改完要重编,编译期能拦下一大批错,运行速度也有保障。编译慢的那几秒,换来运行时的快和写代码时的安全感,这笔账我觉得划算。当然这是我个人的看法,跑小脚本的时候我也用 Python,谁跟自己的时间过不去呢。二、C++ 语言的特点和发展2.1 出身,贝尔实验室的一间办公室1979 年,贝尔实验室,一个叫 Bjarne Stroustrup 的丹麦人想给 C 语言加个 class,让大型程序好写一点。项目先叫 C with classes,意思是带 class 的 C。他的名字国内教程常写成 本贾尼·斯特劳斯特卢普,太长了,后面我就用 本贾尼 这个简称。1983 年改名 C++。这个名字是同事提的,++ 来自 C 的自增运算符,读作 C plus plus,意思是比 C 进一步。这个自带吹嘘味的命名,后来成了行业经典案例。┌─────────────────────────────────────────────────────────────────────┐ │ C++ 的四十年 │ ├─────────────────────────────────────────────────────────────────────┤ │ 从 1979 到现在,一条线看完 │ │ │ │ 1979 贝尔实验室,Bjarne Stroustrup 动手,项目叫 C with classes │ │ │ │ │ 1983 改名 C++,++ 取自 C 的自增运算符,意思是比 C 进一步 │ │ │ │ │ 1985 第一份商业发行,同名的书也在这一年出来 │ │ │ │ │ 1998 第一版国际标准,C++98,模板和 STL 进场 │ │ │ │ │ 2003 小修小补,C++03,没有新语法 │ │ │ │ │ 2011 大改,auto、lambda、智能指针都来了 │ │ │ │ │ 2014 小步前进,C++14 │ │ │ │ │ 2017 继续补强,C++17 │ │ │ │ │ 2020 大版本,concepts、ranges、协程 │ │ │ │ │ 2023 目前最新的标准,工具链支持还在铺开 │ └─────────────────────────────────────────────────────────────────────┘2.2 标准版本,认准 C++11 之后的版本年份看点C++981998第一版国际标准,模板和 STL 进场C++032003修 bug,没有新语法C++112011auto、lambda、智能指针、范围 for,公认的分水岭C++142014泛型 lambda 之类的小补丁C++172017结构化绑定、optional、文件系统库C++202020concepts、ranges、协程C++232023目前最新,工具链支持还在铺开2011 是分水岭。2011 之前的老写法,和新写法放在一起,读起来像两门语言。业界把 2011 之后的叫 modern C++,本书全程按这个来。版本选择不用纠结。写新代码,标准直接上 C++17 或 C++20,g++ 编译时加-std=c++17就行。语法主体十几年没大变,把精力放在编程思想上,别耗在版本对比上。2.3 它强在哪特点说明新手能感受到什么快编译成机器码,零开销抽象是设计目标同样的算法,它常常跑在第一档面向过程也能面向对象class、继承、多态都有先当 C 用,慢慢再体会对象泛型模板让一套代码通吃多种类型STL 的容器和算法都靠它标准库全容器、算法、线程、文件都有现成的不用什么都自己造兼容 C绝大多数 C 代码能直接编老库拿过来就能用跨平台一份源码,各平台各编各的换系统要重编,不用重写拿它干活的领域,都是性能敏感或者离硬件近的地方。领域拿它干什么眼熟的项目游戏引擎与玩法逻辑Unreal Engine浏览器与运行时渲染引擎、JS 引擎Chrome 的 Blink 和 V8数据库存储引擎MySQL、MongoDB嵌入式与汽车固件、控制软件车载系统、机器人金融交易系统高频交易对延迟敏感大型桌面软件性能吃紧的客户端修图软件、办公套件的部分模块丑话说在前面。它不好学。语法细节多,历史包袱重,报错信息一长串,指针那一关能卡住一批人。学会的回报也摆在那儿,性能和就业面都真实存在。想清楚再上车,上车就认真开。2.4 和 C 的关系大体上,C++ 是 C 的超集,绝大多数 C 代码能直接当 C++ 编。少数地方行为不同,比如类型转换的规则,混写时要留意。学 C++ 之前不用先学 C,直接上。遇到看不懂的老式写法,多半是 C 时代留下来的,查一下就知道。三、C++ 程序的基本结构3.1 最小程序,五样东西#include <iostream> using namespace std; int main() { cout << "Hello, World!" << endl; return 0; }这七行是能跑起来的最小单位,拆开看,统共五样东西。┌──────────────────────────────────────────────────────────┐ │ 最小程序的骨架 │ ├──────────────────────────────────────────────────────────┤ │ 七行代码,统共五样东西 │ │ │ │ ┌────────────────────────────────────────────────────┐ │ │ │ #include <iostream> 预处理指令,请工具库进来 │ │ │ │ using namespace std; 标准库的名字可以直接写 │ │ │ │ int main() { 程序入口,系统只从这里开工│ │ │ │ cout << "Hi"; 语句,真正干活的 │ │ │ │ return 0; 告诉系统,正常收工 │ │ │ │ } 函数体到此为止 │ │ │ └────────────────────────────────────────────────────┘ │ │ │ │ 漏哪一样,编译器都会拦你,报错信息就是线索 │ └──────────────────────────────────────────────────────────┘成分长什么样干什么预处理指令#include把工具库的声明请进来命名空间声明using namespace std;标准库的名字可以直接写函数int main() { ... }干活的单元,程序入口也叫 main语句cout << "Hello, World!" << endl;一条命令,分号收尾返回值return 0;告诉操作系统干得怎么样逐个说。include 那行是预处理指令。iostream 是标准库的输入输出组件,cout、endl 都在里面住着。不请它,后面全抓瞎。using namespace std; 是命名空间声明。std 是标准库住的大院,不声明的话,写 cout 要写全名 std::cout。新手阶段先写上,能省一半打字量。等代码有规模了,再讨论要不要省。int main() 是程序入口。系统启动程序,只认 main 这个名字,从第一行花括号开始执行。一个程序只能有一个 main,写两个链接会报错。花括号划出函数体的范围,里面的语句按顺序一条条跑。分号是语句的句号,漏一个,编译器能给你报一串错,而且报错位置经常偏一行,这是 C++ 的老传统。return 0 告诉操作系统,活干完了,没出事,0 是正常收工的暗号。现在 main 里不写 return 也可以,编译器会默认加,写上是老规矩,图个清楚。3.2 注释,写给自己看的注释有两种写法,编译器看见就当没看见。// 单行注释,两个斜线,到此为止 /* 块注释 斜杠星开头,星斜杠结尾,中间随便写 */代码是写给机器的,注释是写给人的。三个月后回来看自己的代码,你会感谢当时写注释的那个人。四、编写第一个 C++ 程序4.1 挑一个工具工具适合谁备注g++想敲命令学的MinGW-w64,或 Linux、macOS 的包管理器Visual StudioWindows 上做项目社区版免费,安装时勾 C++ 工作负载VS Code + C/C++ 扩展轻量编辑器党装扩展,再配上 g++CLion要强力补全和调试收费,学生免费在线编译器只想跑一段代码wandbox、godbolt 之类本章例子用 g++,命令行三行跑完,不挑编辑器。装好之后先验货。C:\> g++ --version g++ (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 8.1.0Windows 上装完 MinGW,要把安装目录下的 bin 加进 PATH,命令行才认 g++。认的话万事大吉,不认的话,把路径加进去,重启命令行再试。4.2 从源码到跑起来,四步新建一个文件,取名 hello.cpp。后缀必须是 .cpp,这是 C++ 源码的招牌,.c 是给 C 的,编译器靠后缀决定按哪套规则来。#include <iostream> using namespace std; int main() { cout << "Hello, World!" << endl; return 0; }存好,编译,一条命令。g++ hello.cpp -o hello-o hello 告诉编译器,产物叫 hello。不写的话,Windows 下默认叫 a.exe,Linux 和 macOS 下默认叫 a.out,都能跑,名字不好记。这一条命令底下,流水线跑了四步。┌───────────────────────────────────────────────────────────┐ │ 编译链接走四步 │ ├───────────────────────────────────────────────────────────┤ │ 一条 g++ 命令,底下跑了四步 │ │ │ │ ┌────────────────────────────────────────────────────┐ │ │ │ 第一步 预处理 展开 include,把头文件拷进来 │ │ │ └────────────────────────────────────────────────────┘ │ │ │ │ │ v │ │ ┌────────────────────────────────────────────────────┐ │ │ │ 第二步 编译 翻成汇编代码 │ │ │ └────────────────────────────────────────────────────┘ │ │ │ │ │ v │ │ ┌────────────────────────────────────────────────────┐ │ │ │ 第三步 汇编 翻成机器码,装进目标文件 │ │ │ └────────────────────────────────────────────────────┘ │ │ │ │ │ v │ │ ┌────────────────────────────────────────────────────┐ │ │ │ 第四步 链接 和标准库拼成可执行文件 │ │ │ └────────────────────────────────────────────────────┘ │ │ │ │ 四步的产物依次是 hello.i、hello.s、hello.o、hello.exe │ └───────────────────────────────────────────────────────────┘阶段干什么产物预处理展开 include,把头文件拷进来hello.i编译翻成汇编代码hello.s汇编翻成机器码,装进目标文件hello.o链接和标准库拼成可执行文件hello / hello.exe想看中间产物,加参数单独跑,每一步都能停下来看。g++ -E hello.cpp -o hello.i # 只做预处理 g++ -S hello.cpp -o hello.s # 做到汇编 g++ -c hello.cpp -o hello.o # 做到目标文件排查离奇问题的时候,这几条能救命。模板那章的报错,一半靠它们看懂。4.3 跑起来┌──────────────────────────────────────────────────────────┐ │ 第一个程序从写到跑 │ ├──────────────────────────────────────────────────────────┤ │ 第一个程序,四步走完 │ │ │ │ ┌────────────────────────────────────────────────────┐ │ │ │ 写源码 新建 hello.cpp,把代码敲进去 │ │ │ └────────────────────────────────────────────────────┘ │ │ │ │ │ v │ │ ┌────────────────────────────────────────────────────┐ │ │ │ 编译 g++ hello.cpp -o hello,一次跑完 │ │ │ └────────────────────────────────────────────────────┘ │ │ │ │ │ v │ │ ┌────────────────────────────────────────────────────┐ │ │ │ 运行 Windows 输 hello,其他系统输 ./hello │ │ │ └────────────────────────────────────────────────────┘ │ │ │ │ │ v │ │ ┌────────────────────────────────────────────────────┐ │ │ │ 看结果 屏幕上出现 Hello, World! │ │ │ └────────────────────────────────────────────────────┘ │ └──────────────────────────────────────────────────────────┘Windows 下在命令行输入 hello,Linux 和 macOS 下输入 ./hello,屏幕上出现 Hello, World! 这一行,第一个程序就算成了。给自己鼓个掌,后面还有一百个程序要写。逐行拆一遍。代码作用#include把输入输出工具库请进来using namespace std;标准库的名字可以直接写int main() {程序入口,系统从这里开工cout << "Hello, World!" << endl;往屏幕上写一句话,endl 是换行加刷新return 0;告诉系统正常收工}函数体结束cout 读作 see out,是标准库的输出对象,可以把它当成屏幕的化身。<< 是往它里面送东西的通道,叫插入运算符。送字符串就显示字符串,送数字就显示数字,送 endl 就换个行。再写一个带输入的,试试 cin。#include <iostream> using namespace std; int main() { int a = 0, b = 0; cout << "请输入两个整数,用空格分开: "; cin >> a >> b; cout << a << " + " << b << " = " << a + b << endl; return 0; }跑起来是这样。请输入两个整数,用空格分开: 3 5 3 + 5 = 8cin 是输入通道,>> 把键盘敲的东西取出来,按变量的类型存好。a 和 b 声明成 int,敲 3 和 5 进去,加起来,原样送出来。这里有个细节,int 存不了小数,也存不下太大的数,超了会溢出。这是下一章的内容,先留个印象。4.4 输出中文乱码,Windows 专属坑程序里写中文,屏幕上出现一串问号或者怪符号,这个坑 Windows 用户躲不掉。┌──────────────────────────────────────────────────────────┐ │ 中文乱码是怎么发生的 │ ├──────────────────────────────────────────────────────────┤ │ 两边编码对不上,字就变了样 │ │ │ │ ┌────────────────────────────────────────────────────┐ │ │ │ 源码存成 UTF-8,字符串按 UTF-8 的字节存 │ │ │ └────────────────────────────────────────────────────┘ │ │ │ │ │ v │ │ ┌────────────────────────────────────────────────────┐ │ │ │ 程序把字节原样送到屏幕 │ │ │ └────────────────────────────────────────────────────┘ │ │ │ │ │ v │ │ ┌────────────────────────────────────────────────────┐ │ │ │ 命令行按 GBK 解码,代码页 936 │ │ │ └────────────────────────────────────────────────────┘ │ │ │ │ │ v │ │ ┌────────────────────────────────────────────────────┐ │ │ │ 对不上,屏幕出现乱码 │ │ │ └────────────────────────────────────────────────────┘ │ │ │ │ 解法三条,让两边用同一种编码 │ └──────────────────────────────────────────────────────────┘源码存成 UTF-8,字符串按 UTF-8 的字节存。程序运行时把字节原样送到屏幕。Windows 简体中文的命令行默认按 GBK 解码,代码页 936。两边对不上,乱码就来了。三条解法,挑一条。命令行切 UTF-8,执行 chcp 65001,再跑程序。切完最好重开窗口。源文件存成 GBK。VS Code 右下角点编码,Save with Encoding,选 GB2312 或 GBK。换 Windows Terminal,默认 UTF-8,一劳永逸,微软商店免费下。Linux 和 macOS 默认 UTF-8,没这个烦恼。4.5 新手常踩的坑现象原因解法syntax error 指到分号分号打成全角换回半角分号error: 'cout' was not declared漏了 include 或 using补上undefined reference to std::cout用 gcc 编 .cpp换 g++程序能编,中文乱码源码 UTF-8,命令行 GBK见 4.4 节error: 'mian' was not declaredmain 拼错改回 main提示不是内部或外部命令g++ 没进 PATH把 bin 目录加进 PATH报错看不懂的时候,把整段错误信息复制去搜。搜索引擎见过的新手坑比你想的多,这个习惯不高级,但是管用。五、小结5.1 本章知识收口表模块你现在应该能做对的事自测语言三代说清机器、汇编、高级的差别□ 能解释为什么高级语言要翻译编译与解释知道 C++ 是编译型以及它带来什么□ 能说出编译四阶段的名字C++ 版本知道 C++11 是分水岭□ 会加 -std=c++17程序结构认出五样成分□ 不看书能默写最小程序第一个程序独立写完编译跑通□ 屏幕上出现 Hello, World!排错常见坑知道往哪查□ 乱码和分号错能自己修┌─────────────────────────────────────────────────────┐ │ 一章走完该带走的东西 │ ├─────────────────────────────────────────────────────┤ │ 学完这一章,兜里该有这五样 │ │ │ │ +--------------+--------------------------------+ │ │ |模块 |带走什么 | │ │ +--------------+--------------------------------+ │ │ |语言三代 |机器、汇编、高级,一代比一代省事| │ │ |编译与解释 |C++ 是编译型,翻一次跑很久 | │ │ |版本 |认准 C++11 之后,加 -std=c++17 | │ │ |程序结构 |include、main、return 五样东西 | │ │ |第一个程序 |写、编、跑加排错 | │ │ +--------------+--------------------------------+ │ │ │ │ 哪一块心里没底,回去找对应小节,别急着往下翻 │ └─────────────────────────────────────────────────────┘5.2 五句话记住本章程序是写给机器的指令清单,语言是中间人,C++ 站在离机器近的那一档。C++ 是编译型,翻一次跑很久,改代码要重编。版本认准 C++11 之后,命令加 -std=c++17。最小程序五样东西,include、命名空间、main、语句、return。编译报错先看第一条,后面那一串常常是它引出来的。5.3 课后练习默写最小程序,不看书,编译跑通,打印 Hello, World!。改 4.3 节的程序,输入两个整数,输出它们的差、积、商,商保留两位小数。打印一份自我介绍,三行,姓名、城市、一句爱好,用 cout 分开写。故意制造三个错,全角分号、漏 include、main 拼错,把报错信息抄下来,对着 4.5 节的表找原因。执行 chcp 65001,跑一次带中文输出的程序,观察乱码消失的过程。附录|命令速查卡想干什么命令看编译器版本g++ --version编译链接一条龙g++ hello.cpp -o hello只做预处理g++ -E hello.cpp -o hello.i只做到汇编g++ -S hello.cpp -o hello.s只做到目标文件g++ -c hello.cpp -o hello.o指定 C++ 标准g++ -std=c++17 hello.cpp -o hello跑起来Windows 输 hello,Linux 和 macOS 输 ./hello命令行切 UTF-8chcp 65001