Skip to content

Commit 2f96cd5

Browse files
committed
fix mathjax
1 parent 51e005d commit 2f96cd5

15 files changed

+573
-36
lines changed

.github/workflows/generate_release_pdf.yml

+2-1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ jobs:
3131
pip install mkdocs
3232
pip install mkdocs-print-site-plugin
3333
pip install mkdocs-macros-plugin
34+
pip install python-markdown-math
3435
3536
- name: Compile Mkdocs document
3637
run: |
@@ -90,7 +91,7 @@ jobs:
9091
9192
- name: Compile PDF document
9293
run: |
93-
node misc/export_to_pdf.js file://$PWD/site/print_page/index.html cppguidebook.pdf "小彭老师的现代 C++ 大典"
94+
node misc/export_to_pdf.js file://$PWD/site/print_page/index.html cppguidebook.pdf "✝️小彭大典✝️"
9495
du -h cppguidebook.pdf
9596
9697
- name: Generate ZIP archive

LICENSE

+437
Large diffs are not rendered by default.

docs/design.md

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1-
# 设计模式
1+
# 设计模式 (未完工)
2+
3+
[TOC]
24

35
不要去指挥下面怎么做!
6+
7+
## 动态类型的缺点
8+
9+
$$x = {-b \pm \sqrt{b^2-4ac} \over 2a}.$$

docs/extra.css

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
h1 {
2+
font-weight: 1000;
3+
margin: 1em auto;
4+
}
5+
h2 {
6+
font-weight: 800;
7+
margin: 1em auto;
8+
}
9+
h3 {
10+
font-weight: 600;
11+
margin: 1em auto;
12+
}
13+
h4 {
14+
font-weight: 500;
15+
margin: 1em auto;
16+
}
17+
body {
18+
font-weight: 350;
19+
}
20+
blockquote {
21+
color: var(--bs-gray-600);
22+
}

docs/extra.js

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
var footer = document.querySelector('footer');
2+
footer.innerHTML = footer.innerHTML.replace('<p>Documentation built with <a href="https://www.mkdocs.org/">MkDocs</a>.</p>', '<p style="display: none">本页面由 <a href="https://www.mkdocs.org/">MkDocs</a> 构建</p>');
3+
4+
var lut = {
5+
Next: '下一章',
6+
Previous: '上一章',
7+
Search: '搜索',
8+
'Edit on GitHub': '编辑此页面',
9+
};
10+
11+
// enumerate all nav-links
12+
var links = document.querySelectorAll('a.nav-link');
13+
for (var i = 0; i < links.length; i++) {
14+
var link = links[i];
15+
for (var j = 0; j < link.childNodes.length; j++) {
16+
var node = link.childNodes[j];
17+
if (node.data !== undefined) {
18+
var key = node.data.trim();
19+
var to = lut[key];
20+
if (to !== undefined) {
21+
node.data = node.data.replace(key, to);
22+
} else {
23+
console.log({a: node.data});
24+
}
25+
}
26+
}
27+
}

docs/functional.md

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# 函数式编程
22

3+
[TOC]
4+
35
## 为什么需要函数?
46

57
```cpp

docs/functions.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
# 自定义函数 (未完工)
1+
# 认识函数 (未完工)
2+
3+
[TOC]
4+
5+
# 自定义函数
26

37
函数可以没有返回值,只需要返回类型写 `void` 即可,这样的函数调用的目的只是为了他的副作用(如修改全局变量,输出文本到控制台,修改引用参数等)。
48

docs/helloworld.md

+8-10
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# 你好,世界
22

3+
[TOC]
4+
35
## 什么是函数
46

57
* 函数: 一段用 `{}` 包裹的代码块,有一个独一无二的名字做标识。函数可以被其他函数调用。函数可以有返回值和参数。函数的 `{}` 代码块内的程序代码,每次该函数被调用时都会执行。
@@ -13,15 +15,15 @@ int compute()
1315

1416
上面的代码中,`compute` 就是函数的名字,`int` 表示函数的返回类型——整数。
1517

16-
> {{ icon.tip }} 乃取整数之英文“integer”的“int”而得名(模仿侯捷老师说话)
18+
> {{ icon.fun }} 乃取整数之英文“integer”的“int”而得名(模仿侯捷老师说话)
1719
1820
`{}` 包裹的是函数体,是函数被调用时会执行的代码。
1921

2022
此处 `return 42` 就是函数体内的唯一一条语句,表示函数立即执行完毕,返回 42。
2123

2224
* 返回值: 当一个函数执行完毕时,会向调用该函数的调用者返回一个值,这个值就是 `return` 后面的表达式的值。返回值可以有不同的类型,此处 `compute` 的返回类型是 `int`,也就是说 `compute` 需要返回一个整数。
2325

24-
> {{ icon.tip }} 关于函数的参数我们稍后再做说明
26+
> {{ icon.tip }} 关于函数的参数,我们稍后再做说明
2527
2628
## 从 main 函数说起
2729

@@ -39,7 +41,7 @@ int main()
3941

4042
> {{ icon.detail }} 严格来说,是 C++ 运行时调用了 `main` 函数,但目前先理解为“操作系统调用了 `main` 函数”也无妨。
4143
42-
要把程序发展壮大,我们可以让 `main` 函数调用其他函数,也可以直接在 `main` 函数中编写整个程序的逻辑(不推荐)。
44+
要把程序发展壮大,我们可以让 `main` 函数继续调用其他函数,也可以直接在 `main` 函数中编写整个程序的逻辑(不推荐)。
4345

4446
> {{ icon.fun }} 因此,`main` 可以被看作是“宇宙大爆炸”。
4547
@@ -60,9 +62,7 @@ main 函数总是返回一个整数 (`int` 类型),用这个整数向操作系
6062

6163
返回一个不为 0 的整数可以表示程序出现了异常,是因为出错了才退出的,值的多少可以用于表明错误的具体原因。
6264

63-
> {{ icon.fun }}
64-
操作系统:我调用了你这个程序的 main 函数,我好奇程序是否正确执行了?让我们约定好:如果你运转正常的话,就返回0表示成功哦!如果有错误的话,就返回一个错误代码,比如返回1表示无权限,2表示找不到文件……之类的。当然,错误代码都是不为0的。
65-
65+
> {{ icon.fun }} 操作系统:我调用了你这个程序的 main 函数,我好奇程序是否正确执行了?让我们约定好:如果你运转正常的话,就返回0表示成功哦!如果有错误的话,就返回一个错误代码,比如返回1表示无权限,2表示找不到文件……之类的。当然,错误代码都是不为0的。
6666
6767
## 这个黑色的窗口是?
6868

@@ -112,8 +112,7 @@ int main()
112112
}
113113
```
114114

115-
> {{ icon.fun }}
116-
(\**编译器脸红中\**)
115+
> {{ icon.fun }} (\**编译器脸红中\**)
117116
118117
C++ 支持行注释 `// xx` 和块注释 `/* xx */` 两种语法。
119118

@@ -132,6 +131,5 @@ int main()
132131
}
133132
```
134133

135-
> {{ icon.tip }}
136-
在我们以后的案例代码中,都会像这样注释说明,充当**就地讲解员**的效果。去除这些注释并不影响程序的正常运行,添加文字注释只是小彭老师为了提醒你每一行的代码作用。
134+
> {{ icon.tip }} 在我们以后的案例代码中,都会像这样注释说明,充当**就地讲解员**的效果。去除这些注释并不影响程序的正常运行,添加文字注释只是小彭老师为了提醒你每一行的代码作用。
137135

docs/index.md

+6-4
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,19 @@
1818
1919
可以按顺序阅读,也可以在本页面上方导航栏的“章节列表”中,选择感兴趣的章节阅读。
2020

21-
本书完全开源和免费,GitHub 仓库:[https://github.com/parallel101/cppguidebook](https://github.com/parallel101/cppguidebook)
21+
本书完全开源和免费,GitHub 仓库:[{{ config.repo_url }}]({{ config.repo_url }})
2222

2323
> {{ icon.warn }} 如果你是在付费群中“买”到本书,或者打着小彭老师名号卖课,说明你可能是私有制的受害者。因为小彭老师从来没有付费才能看的课程,所有小彭老师课程都对全球互联网开放。
2424
25-
如需离线查看,可以前往 [GitHub Release 页面](https://github.com/parallel101/cppguidebook/releases) 下载 PDF 文件。
25+
如需离线查看,可以前往 [GitHub Release 页面]({{ config.repo_url }}/releases) 下载 PDF 文件。
2626

27-
如果你在阅读过程中遇到任何问题,可以在 [GitHub Issues](https://github.com/parallel101/cppguidebook/issues) 中提出,小彭老师会尽力解答。
27+
如果你在阅读过程中遇到任何问题,可以在 [GitHub Issues]({{ config.repo_url }}/issues) 中提出,小彭老师会尽力解答。
2828

2929
也可以在 [B 站](https://space.bilibili.com/263032155) 发私信给小彭老师哦。
3030

31-
> {{ icon.tip }} 本书还在持续更新中……要追番的话,可以在 [GitHub](https://github.com/parallel101/cppguidebook) 点一下右上角的 “Watch” 按钮,每当小彭老师提交新 commit,GitHub 会向你发送一峰电子邮件,提醒你小彭老师更新了。
31+
> {{ icon.tip }} 本书还在持续更新中……要追番的话,可以在 [GitHub]({{ config.repo_url }}) 点一下右上角的 “Watch” 按钮,每当小彭老师提交新 commit,GitHub 会向你发送一峰电子邮件,提醒你小彭老师更新了。
32+
33+
更新时间:{{ config.repo_url }}
3234

3335
## 格式约定
3436

docs/platform.md

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# 开发环境与平台选择
22

3+
[TOC]
4+
35
TODO
46

57
## IDE 不是编译器!

docs/unicode.md

+13-9
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
# 字符编码那些事
22

3+
[TOC]
4+
35
## 字符集
46

7+
计算机不能直接存储字符,而是用数字来代替,这就是字符集,为每个字符指定一个数字。
8+
59
### ASCII
610

711
ASCII 为英文字母、阿拉伯数组、标点符号等 128 个字符,每个都用一个 0 到 127 范围内的数字对应。
@@ -175,7 +179,7 @@ UTF-8 把一个码点序列化为一个或多个码位,一个码位用 1 至 4
175179

176180
序列化规则如下:
177181

178-
#### 0 到 0x7F
182+
#### 兼容 ASCII
179183

180184
对于 0 到 0x7F 的字符,这个范围的字符需要 7 位存储。
181185

@@ -255,7 +259,7 @@ UTF-8 的构造就像一列小火车一样,不同范围内的码位会被编
255259

256260
> {{ icon.fun }} 高级车头装了防弹钢板,载客空间变少,只好匀到后面的车厢。
257261
258-
#### UTF-8 的抗干扰机制
262+
#### UTF-8 的抗干扰能力
259263

260264
如果发现 `10` 开头的独立车厢,就说明出问题了,可能是火车被错误拦腰截断,也可能是字符串被错误地反转。因为 `10` 只可能是火车车厢,不可能出现在火车头部。此时解码器应产生一个报错,或者用错误字符“�”替换。
261265

@@ -961,7 +965,7 @@ std::string s = u8"你好";
961965
- 应用场景:常见于文字处理需求不大,但有强烈的跨平台需求,特别是互联网方面的软件。他们通常只用到字符串的拼接、查找、切片通常也只是在固定的位置(例如文件分隔符 `'/'`)。也非常适合主要面对的是以 ASCII 为主的“代码”类文本,UTF-8 是对英文类文本压缩率最高的,所以也广泛用于编译器、数据库之类的场景。同时因为 UTF-8 完全兼容 ASCII,使得他能轻易适配远古的 C 语言程序和库。
962966
- 方法:始终以 UTF-8 编码存储和处理字符串。
963967
- 优点:跨平台,在网络传输时无需任何转码,UTF-8 是互联网的主流编码格式,不同平台上运行的 UTF-8 软件可以随意共享文本数据。兼容 ASCII,方便复用现有库和生态。对英文类文本压缩率高,对中文文本也不算太差。
964-
- 缺点:对于底层 API 均采用 UTF-16 的 Windows 系统,需要进行字符编码转换,有少量性能损失。且字符串的正确切片、求长度等操作的复杂度会变成 $O(N)$ 而不是通常的 $O(1)$。
968+
- 缺点:对于底层 API 均采用 UTF-16 的 Windows 系统,需要进行字符编码转换,有少量性能损失。且字符串的正确切片、求长度等操作的复杂度会变成 $$$O(N)$$$ 而不是通常的 $$O(1)$$
965969
- 代表作:Rust 语言、Go 语言、CMake 构建系统、Julia 语言等。
966970

967971
在 C++ 中,可以通过 `u8"你好"` 创建一个保证内部是 UTF-8 编码的字符串常量,类型为 `char8_t []`
@@ -1049,7 +1053,7 @@ Rust 和 Go:严格区分“字符 (32 位)”和“字节 (8 位)”的概念
10491053
|Go|`rune`|`byte`|
10501054
|Julia|`Char`|`UInt8`|
10511055

1052-
为此,这些语言都为字符串提供了两套 API,一种是按字符索引,一种是按字节索引。按字符索引时,会从头开始,逐个解析码位,直到解析到想要的字符为止,复杂度 $O(N)$。按字节索引时,直接跳到指定字节,无需解析,复杂度 $O(1)$。
1056+
为此,这些语言都为字符串提供了两套 API,一种是按字符索引,一种是按字节索引。按字符索引时,会从头开始,逐个解析码位,直到解析到想要的字符为止,复杂度 $$$O(N)$$$。按字节索引时,直接跳到指定字节,无需解析,复杂度 $$O(1)$$
10531057

10541058
```rust
10551059
let s = "你好";
@@ -1150,7 +1154,7 @@ for (char32_t c : Utf8Range(s)) {
11501154
- 应用场景:通常认为,UTF-16 是纯粹的历史遗留糟粕,新软件不应该再使用 UTF-16。只有在和这些糟粕软件的 API 打交道时,才必须转换为 UTF-16。但也有人指出:UTF-16 是纯中文压缩率最高的编码格式,所以 UTF-16 还比较适合纯中文或以中文内容为主的文本数据压缩。
11511155
- 方法:始终以 UTF-16 编码存储和处理字符串。
11521156
- 优点:调用 Windows 系统 API 时无需任何转换,直接就能调用,最适合 Windows 本地开发,非跨平台。且对纯中文内容可比 UTF-8 额外节省 33% 空间。
1153-
- 缺点:对于 Windows 以外的系统就需要转换回 UTF-8,有少量性能开销。且如果存储的内容主要是纯英文,如 XML 代码等,内存占用会比 UTF-8 翻倍。而且 UTF-16 仍然是变长编码,虽然出现变长的概率较低,但不为 0,仍需要开发者做特殊处理。字符串的按码位反转会导致生僻字符出错,字符串以码点为单位的的正确切片、求长度等操作的复杂度仍然 $O(N)$ 而不是通常的 $O(1)$。并且 UTF-16 有大小端转换的问题。
1157+
- 缺点:对于 Windows 以外的系统就需要转换回 UTF-8,有少量性能开销。且如果存储的内容主要是纯英文,如 XML 代码等,内存占用会比 UTF-8 翻倍。而且 UTF-16 仍然是变长编码,虽然出现变长的概率较低,但不为 0,仍需要开发者做特殊处理。字符串的按码位反转会导致生僻字符出错,字符串以码点为单位的的正确切片、求长度等操作的复杂度仍然 $$$O(N)$$$ 而不是通常的 $$O(1)$$。并且 UTF-16 有大小端转换的问题。
11541158
- 代表作:Windows 系统 API、Java 语言、Windows 文件系统 (NTFS)、Qt、Word、JSON,他们都是 UTF-16 的受害者。
11551159
11561160
这相当于是把 UTF-16 当作了内码,但 UTF-16 依然是一种变长编码,对常见的中文处理没问题,生僻字就容易出问题,且因为出现概率低,很容易不发现,埋下隐患。
@@ -1213,7 +1217,7 @@ fmt::println("{}", s.size()); // 3
12131217
12141218
- 应用场景:适合需要经常处理文字的领域,如文本编辑器、浏览器等。但不适合存储和传输,因为浪费硬盘和网络带宽。字符串一般都长期以 UTF-8 存储,只有在需要频繁索引码位时,才需要转换为 UTF-32。
12151219
- 方法:始终以 UTF-32 编码存储和处理字符串。
1216-
- 优点:字符串的按码位反转、切片、求长度等操作都是 $O(1)$ 的复杂度,可以当作普通数组一样,随意处理。例如你可以设想一个文本编辑框,需要支持“退格”操作,如果是 UTF-8 和 UTF-16 就需要繁琐的判断代理对、各种车厢,而 UTF-32 的字符串只需要一次 `pop_back` 就搞定了。
1220+
- 优点:字符串的按码位反转、切片、求长度等操作都是 $$$O(1)$$$ 的复杂度,可以当作普通数组一样,随意处理。例如你可以设想一个文本编辑框,需要支持“退格”操作,如果是 UTF-8 和 UTF-16 就需要繁琐的判断代理对、各种车厢,而 UTF-32 的字符串只需要一次 `pop_back` 就搞定了。
12171221
- 缺点:浪费空间大,通常在保存时,仍然需要转换回 UTF-8 后再写入文件,有一定性能开销。
12181222
12191223
*总结:要支持 UTF-32 阵营,请全部使用 `char32_t` 和 `std::u32string`。字面量全用 `U"你好"` 的形式书写,读文件时转为 UTF-32,写文件时转回 UTF-8。*
@@ -1823,7 +1827,7 @@ TODO
18231827
//
18241828
//缺点是这样的软件会无法跨平台,因为 `wchar_t` 在 Linux 上是安全的内码 UTF-32。而 Windows 上是 UTF-16,是不定长的编码,如果存在“𰻞”和“😉”这样超过 0x10000 的生僻字,就会产生两个 `wchar_t`!如果文字处理涉及切片,就会出问题。概率很低,但不为零,软件仍然需要对可能存在的双 `wchar_t` 做特殊处理。若不处理,轻则乱码,重则留下漏洞,被黑客攻击,加重了 Windows 和 Java 程序员的心智负担。
18251829
//
1826-
//如果一个程序(例如 GCC)只适配了 `wchar_t` 是 UTF-32 的平台,想当然的把 `wchar_t` 当作安全的定长内码使用,那移植到 Windows 上后就会丧失处理“𰻞”和“😉”的能力。要么就需要对所有代码大改,把原本 $O(1)$ 的字符串求长度改成 $O(N)$ 的;要么出现乱码,被黑客攻击。
1830+
//如果一个程序(例如 GCC)只适配了 `wchar_t` 是 UTF-32 的平台,想当然的把 `wchar_t` 当作安全的定长内码使用,那移植到 Windows 上后就会丧失处理“𰻞”和“😉”的能力。要么就需要对所有代码大改,把原本 $$$O(1)$$$ 的字符串求长度改成 $$O(N)$$ 的;要么出现乱码,被黑客攻击。
18271831
//
18281832
//当需要读写二进制文件时,使用 `fstream`,原封不动地按“字节”为单位读取。
18291833
//
@@ -2293,7 +2297,7 @@ stream << "你好,世界\n"; // 写入 QString,QTextStream 会自动将其
22932297
22942298
如果用 UTF-8 或 UTF-16 来存储的话,会遇到变长编码的固有缺陷:
22952299
2296-
例如像字符串索引,字符串求长度等操作,要么索引出来的是字节而不是字符了;要么就需要 $O(N)$ 的复杂度,逐一遍历每个字节,才能确定真正的位置;哪怕全是 ASCII 也得这么做,因为万一刚好有一个是中文字符呢?
2300+
例如像字符串索引,字符串求长度等操作,要么索引出来的是字节而不是字符了;要么就需要 $$$O(N)$$$ 的复杂度,逐一遍历每个字节,才能确定真正的位置;哪怕全是 ASCII 也得这么做,因为万一刚好有一个是中文字符呢?
22972301
22982302
所以,对于经常需要处理字符串的 Python 来说,UTF-8 是无法接受的,似乎只能以 UTF-32 来存储?
22992303
@@ -2340,7 +2344,7 @@ struct PyUnicodeString {
23402344

23412345
### Rust `&str``String`
23422346

2343-
而 Rust 则采用了字符串全员 UTF-8 的策略,这是因为 Rust 最常用于互联网方面的底层系统软件,互联网最常用的文本编码就是 UTF-8,没有大小端问题,且国际通用。除此之外,互联网基建最常见的平台就是 Linux,使用 UTF-8 存储字符串,调用 Linux 系统 API 无需任何转换。且文本文件基本都可以假定是 UTF-8 编码,写入时无需任何转换,复杂度低至 $O(1)$。作为代价,这导致文本处理上的一些困难,例如字符串的索引,需要区分是按字节索引还是按字符索引,如果确实需要按字符索引的话,复杂度就会是 $O(N)$ 了。
2347+
而 Rust 则采用了字符串全员 UTF-8 的策略,这是因为 Rust 最常用于互联网方面的底层系统软件,互联网最常用的文本编码就是 UTF-8,没有大小端问题,且国际通用。除此之外,互联网基建最常见的平台就是 Linux,使用 UTF-8 存储字符串,调用 Linux 系统 API 无需任何转换。且文本文件基本都可以假定是 UTF-8 编码,写入时无需任何转换,复杂度低至 $$$O(1)$$$。作为代价,这导致文本处理上的一些困难,例如字符串的索引,需要区分是按字节索引还是按字符索引,如果确实需要按字符索引的话,复杂度就会是 $$O(N)$$ 了。
23442348

23452349
无论如何,如果你选择了 UTF-8 流派的话,Rust 字符串的“迭代器双轨制”确实值得称道:
23462350

docs/vartypes.md

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
# 变量与类型 (未完工)
22

3+
[TOC]
4+
35
TODO

0 commit comments

Comments
 (0)