diff --git a/.idea/markdocs.iml b/.idea/markdocs.iml
index f3739dd..a4fcc6d 100644
--- a/.idea/markdocs.iml
+++ b/.idea/markdocs.iml
@@ -4,7 +4,7 @@
-
+
\ No newline at end of file
diff --git a/.idea/material_theme_project_new.xml b/.idea/material_theme_project_new.xml
new file mode 100644
index 0000000..6b50a0f
--- /dev/null
+++ b/.idea/material_theme_project_new.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 71005d3..6838522 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -3,5 +3,5 @@
-
+
\ No newline at end of file
diff --git a/docs/Cpp-Primer-exercise/1.开始.md b/docs/Cpp-Primer-exercise/1.开始.md
new file mode 100644
index 0000000..4690342
--- /dev/null
+++ b/docs/Cpp-Primer-exercise/1.开始.md
@@ -0,0 +1,476 @@
+---
+sort: 1
+---
+
+# 开始
+
+## 练习1.1
+查阅你使用的编译器的文档,确定它所使用的文件名约定。编译并运行第2页的main程序。
+
+解:
+- ``g++ 1.1.cpp -o main``
+- ``./main``
+
+## 练习1.2
+改写程序,让它返回-1。返回值-1通常被当做程序错误的标识。重新编译并运行你的程序,观察你的系统如何处理main返回的错误标识。
+
+解:
+- 在ubuntu下,使用g++,返回-1,``./main``没有发现任何异常。
+- ``echo $?``,返回255。
+
+## 练习1.3
+编写程序,在标准输出上打印Hello, World。
+
+解:
+
+```cpp
+#include
+
+int main()
+{
+ std::cout << "Hello,World" << std::endl;
+ return 0;
+}
+```
+
+## 练习1.4
+我们的程序使用加法运算符`+`来将两个数相加。编写程序使用乘法运算符`*`,来打印两个数的积。
+
+解:
+```cpp
+#include
+
+int main() {
+ std::cout << "Enter two numbers" << std::endl;
+ int v1 = 0, v2 = 0;
+ std::cin>> v1 >> v2;
+ std::cout << "The product of " << v1 << " and " << v2 << " is "<< v1 * v2 << std::endl;
+}
+```
+
+## 练习1.5
+我们将所有的输出操作放在一条很长的语句中,重写程序,将每个运算对象的打印操作放在一条独立的语句中。
+
+解:
+```cpp
+#include
+
+int main()
+{
+ std::cout << "Enter two numbers" << std::endl;
+ int v1 = 0, v2 = 0;
+ std::cin >> v1 >> v2;
+ std::cout << "The product of ";
+ std::cout << v1;
+ std::cout << " and ";
+ std::cout << v2;
+ std::cout << " is ";
+ std::cout << v1 * v2;
+ std::cout << std::endl;
+}
+```
+
+## 练习1.6
+解释下面程序片段是否合法。
+```cpp
+std::cout << "The sum of " << v1;
+ << " and " << v2;
+ << " is " << v1 + v2 << std::endl;
+```
+如果程序是合法的,它的输出是什么?如果程序不合法,原因何在?应该如何修正?
+
+解:
+
+程序不合法,有多余的分号,修改如下:
+```cpp
+std::cout << "The sum of " << v1
+ << " and " << v2
+ << " is " << v1 + v2 << std::endl;
+```
+
+## 练习1.7
+编译一个包含不正确的嵌套注释的程序,观察编译器返回的错误信息。
+
+解:
+```cpp
+/* 正常注释 /* 嵌套注释 */ 正常注释*/
+```
+错误信息:
+```
+ /* 正常注释 /* 嵌套注释 */ 正常注释*/
+ ^
+ch1.cpp:97:37: error: stray ‘\255’ in program
+ch1.cpp:97:37: error: stray ‘\243’ in program
+ch1.cpp:97:37: error: stray ‘\345’ in program
+ch1.cpp:97:37: error: stray ‘\270’ in program
+ch1.cpp:97:37: error: stray ‘\270’ in program
+ch1.cpp:97:37: error: stray ‘\346’ in program
+ch1.cpp:97:37: error: stray ‘\263’ in program
+ch1.cpp:97:37: error: stray ‘\250’ in program
+ch1.cpp:97:37: error: stray ‘\351’ in program
+ch1.cpp:97:37: error: stray ‘\207’ in program
+ch1.cpp:97:37: error: stray ‘\212’ in program
+ch1.cpp: In function ‘int main()’:
+ch1.cpp:97:50: error: expected primary-expression before ‘/’ token
+ /* 正常注释 /* 嵌套注释 */ 正常注释*/
+ ^
+ch1.cpp:98:5: error: expected primary-expression before ‘return’
+ return 0;
+ ^
+```
+
+## 练习1.8
+指出下列哪些输出语句是合法的(如果有的话):
+```cpp
+std::cout << "/*";
+std::cout << "*/";
+std::cout << /* "*/" */;
+std::cout << /* "*/" /* "/*" */;
+```
+预测编译这些语句会产生什么样的结果,实际编译这些语句来验证你的答案(编写一个小程序,每次将上述一条语句作为其主体),改正每个编译错误。
+
+解:
+
+只有第三句编译出错,改成如下即可:
+```cpp
+std::cout << /* "*/" */";
+```
+第四句等价于输出 `" /* "`。
+
+## 练习1.9
+
+编写程序,使用`while`循环将50到100整数相加。
+
+解:
+```cpp
+#include
+
+int main() {
+ int i = 50, sum = 0;
+ while (i <= 100) {
+ sum += i;
+ i += 1;
+ }
+ std::cout << "sum is " << sum << std::endl;
+
+}
+```
+
+## 练习1.10
+除了`++`运算符将运算对象的值增加1之外,还有一个递减运算符`--`实现将值减少1.编写程序与,使用递减运算符在循环中按递减顺序打印出10到0之间的整数。
+
+解:
+```cpp
+#include
+
+int main() {
+ int i = 10;
+ while (i >= 0) {
+ std::cout << i << std::endl;
+ i--;
+ }
+}
+```
+
+## 练习1.11
+编写程序,提示用户输入两个整数,打印出这两个整数所指定的范围内的所有整数。
+
+解:
+```cpp
+#include
+
+int main() {
+ int temp = 0;
+ std::cout << "Please enter two numbers"<< std::endl;
+ int v1 = 0, v2 = 0;
+ std::cin >> v1 >> v2;
+ // 若v1 > v2,则调换顺序
+ if (v1 > v2) {
+ temp = v2;
+ v2 = v1;
+ v1 = temp;
+ }
+
+ while (v1 <= v2){
+ std::cout << v1 << std::endl;
+ v1 += 1;
+ }
+}
+```
+
+## 练习1.12
+
+下面的for循环完成了什么功能?sum的终值是多少?
+```cpp
+int sum = 0;
+for (int i = -100; i <= 100; ++i)
+ sum += i;
+```
+
+解:
+
+从-100加到100,sum的终值是0。
+
+## 练习1.13
+使用for循环重做1.4.1节中的所有练习(练习1.9到1.11)。
+
+解:
+
+### 练习1.9
+
+```cpp
+#include
+// 修改1.9
+int main() {
+ int sum = 0;
+ for (int i = 50; i <= 100; i++) {
+ sum += i;
+ }
+ std::cout << "sum is " << sum << std::endl;
+}
+```
+
+### 练习1.10
+
+```cpp
+#include
+
+int main() {
+ for (int i = 10; i >= 0; i--) {
+ std::cout << i << std::endl;
+ }
+}
+
+
+```
+
+### 练习1.11
+
+```cpp
+#include
+
+int main() {
+ int temp = 0;
+ std::cout << "Please enter two numbers"<< std::endl;
+ int v1 = 0, v2 = 0;
+ std::cin >> v1 >> v2;
+ // 若v1 > v2,则调换顺序
+ if (v1 > v2) {
+ temp = v2;
+ v2 = v1;
+ v1 = temp;
+ }
+
+ for (;v1 <= v2;v1++){
+ std::cout << v1 << std::endl;
+ }
+}
+
+```
+
+## 练习1.14
+对比for循环和while循环,两种形式的优缺点各是什么?
+
+解:
+-
+-
+```
+The main difference between the `for`'s and the `while`'s is a matter of pragmatics:
+we usually use `for` when there is a known number of iterations,
+and use `while` constructs when the number of iterations in not known in advance.
+The `while` vs `do ... while` issue is also of pragmatics,
+the second executes the instructions once at start,
+and afterwards it behaves just like the simple `while`.
+```
+
+
+## 练习1.15
+编写程序,包含第14页“再探编译”中讨论的常见错误。熟悉编译器生成的错误信息。
+
+解:
+
+编译器可以检查出的错误有:
+- 语法错误
+- 类型错误
+- 声明错误
+
+## 练习1.16
+编写程序,从cin读取一组数,输出其和。
+
+解:
+
+```cpp
+#include
+
+int main() {
+ int sum = 0, value = 0;
+ while(std::cin >> value) {
+ sum += value;
+ }
+ std::cout << "sum is " << sum << std::endl;
+ return 0;
+}
+
+```
+
+## 练习1.17
+如果输入的所有值都是相等的,本节的程序会输出什么?如果没有重复值,输出又会是怎样的?
+
+
+## 练习1.18
+编译并运行本节的程序,给它输入全都相等的值。再次运行程序,输入没有重复的值。
+
+解:
+
+
+
+
+
+## 练习1.19
+修改你为1.4.1节练习1.11(第11页)所编写的程序(打印一个范围内的数),使其能处理用户输入的第一个数比第二个数小的情况。
+
+解:
+
+```cpp
+#include
+
+int main()
+{
+ int start = 0, end = 0;
+ std::cout << "Please input two num: ";
+ std::cin >> start >> end;
+ if (start <= end) {
+ while (start <= end){
+ std::cout << start << " ";
+ ++start;
+ }
+ std::cout << std::endl;
+ }
+ else{
+ std::cout << "start should be smaller than end !!!";
+ }
+}
+```
+
+## 练习1.20
+在网站http://www.informit.com/title/032174113 上,第1章的代码目录包含了头文件 Sales_item.h。将它拷贝到你自己的工作目录中。用它编写一个程序,读取一组书籍销售记录,将每条记录打印到标准输出上。
+
+解:
+
+```cpp
+#include
+#include "Sales_item.h"
+
+int main()
+{
+ for (Sales_item item; std::cin >> item; std::cout << item << std::endl);
+ return 0;
+}
+```
+
+命令:
+```
+./main < data/add_item
+```
+
+输出:
+```
+0-201-78345-X 3 60 20
+0-201-78345-X 2 50 25
+```
+
+## 练习1.21
+编写程序,读取两个 ISBN 相同的 Sales_item 对象,输出他们的和。
+
+解:
+
+```cpp
+#include
+#include "Sales_item.h"
+
+int main()
+{
+ Sales_item item_1;
+ Sales_item item_2;
+ std::cin >> item_1;
+ std::cout << item_1 << std::endl;
+ std::cin >> item_2;
+ std::cout << item_2 << std::endl;
+ std::cout << "sum of sale items: " << item_1 + item_2 << std::endl;
+ return 0;
+}
+```
+
+命令:
+```
+./main < data/add_item
+```
+
+输出:
+```
+0-201-78345-X 3 60 20
+0-201-78345-X 2 50 25
+sum of sale items: 0-201-78345-X 5 110 22
+```
+
+## 练习1.22
+编写程序,读取多个具有相同 ISBN 的销售记录,输出所有记录的和。
+
+解:
+
+```cpp
+#include
+#include "Sales_item.h"
+
+int main() {
+ Sales_item sum_item;
+
+ for (Sales_item item; std::cin >> item; std::cout << item << std::endl) {
+ sum_item += item;
+ }
+
+ std::cout << "sum is " << sum_item << std::endl;
+ return 0;
+}
+```
+
+
+## 练习1.23
+编写程序,读取多条销售记录,并统计每个 ISBN(每本书)有几条销售记录。
+
+## 练习1.24
+输入表示多个 ISBN 的多条销售记录来测试上一个程序,每个 ISBN 的记录应该聚在一起。
+
+解:
+
+```cpp
+#include
+#include "Sales_item.h"
+
+int main()
+{
+ Sales_item total;
+ if (std::cin >> total){
+ Sales_item trans;
+ while (std::cin >> trans){
+ if (total.isbn() == trans.isbn()) {
+ total += trans;
+ }
+ else {
+ std::cout << total << std::endl;
+ total = trans;
+ }
+ }
+ std::cout << total << std::endl;
+ }
+ else {
+ std::cerr << "No data?!" << std::endl;
+ return -1;
+ }
+ return 0;
+}
+```
+
+```
+## 练习1.25
+借助网站上的`Sales_item.h`头文件,编译并运行本节给出的书店程序。
diff --git a/docs/Cpp-Primer-exercise/2.变量和基本类型.md b/docs/Cpp-Primer-exercise/2.变量和基本类型.md
new file mode 100644
index 0000000..d3d30c6
--- /dev/null
+++ b/docs/Cpp-Primer-exercise/2.变量和基本类型.md
@@ -0,0 +1,774 @@
+---
+sort: 2
+---
+
+# 变量和基本类型
+
+## 练习2.1
+类型 int、long、long long 和 short 的区别是什么?无符号类型和带符号类型的区别是什么?float 和 double的区别是什么?
+
+解:
+
+在C++中,int、long、long long和short都属于整型,区别是C++规定的尺寸的最小值(即该类型在内存中所占的比特数)不同,其中, short 和 int 至少16位,long 至少32位,long long 至少64位。 C++标准允许不同的编译器赋予这些类型更大的尺寸。某一类型栈的比特数不同,它所能表示的数据范围也不一样。
+
+大多数整数都能划分为无符号类型和带符号类型,在无符号类型中所有比特都用来存储数值,但只能表示大于等于0的值,带符号类型则可以表示正数、负数或0.
+
+float 和 double 分别是单精度浮点数和双精度浮点数,区别主要是在内存中所占的比特数不同,以及默认规定的有效位数不同。
+
+## 练习2.2
+计算按揭贷款时,对于利率、本金和付款分别应选择何种数据类型?说明你的理由。
+
+解:
+
+在实际应用中,利率、本金和付款既有可能是整数,也有可能是普通的实数。因此应该选择一种浮点类型来表示。在三种可供选择的浮点类型float、double和long double中double和float的计算代价比较接近且表示范围更广,long double的计算代价则相对较大,一般情况下没有选择的必要。综合以上分析,选择 double 是比较恰当的。
+
+## 练习2.3
+读程序写结果。
+```cpp
+unsigned u = 10, u2 = 42;
+std::cout << u2 - u << std::endl;
+std::cout << u - u2 << std::endl;
+int i = 10, i2 = 42;
+std::cout << i2 - i << std::endl;
+std::cout << i - i2 << std::endl;
+std::cout << i - u << std::endl;
+std::cout << u - i << std::endl;
+```
+
+解:
+
+输出:
+```
+32
+4294967264
+32
+-32
+0
+0
+```
+
+0
+u和u2都是无符号整数,因此u2-u得到了正确的结果(42-10=32);u-u2也能正确计算,但是因为直接计算的结果是-32,所以在表示为无符号整数时自动加上模,在作者的编译环境中int占32位,因此加模的结果是 4294967264。
+
+i和i2 都是带符号整数,因此中间两个式子的结果比较直观,42-10=32,10-42=-32。
+
+在最后两个式子中,u和i分别是无符号整数和带符号整数,计算时编译器先把带符号数转换为无符号数,幸运的是,i本身是一个正数,因此转换后不会出现异常情况,两个式子的计算结果都是0。
+
+不过需要提醒读者注意的是,一般情况下请不要在同一个表达式中混合使用无符号类型和带符号类型。因为计算前带符号类型会自动转换成无符号类型,当带符号类型取值为负时就会出现异常结果。
+
+
+## 练习2.4
+编写程序检查你的估计是否正确,如果不正确,请仔细研读本节直到弄明白问题所在。
+
+## 练习2.5
+指出下述字面值的数据类型并说明每一组内几种字面值的区别:
+```
+(a) 'a', L'a', "a", L"a"
+(b) 10, 10u, 10L, 10uL, 012, 0xC
+(c) 3.14, 3.14f, 3.14L
+(d) 10, 10u, 10., 10e-2
+```
+
+解:
+- (a): 字符字面值,宽字符字面值,字符串字面值,宽字符串字面值。
+- (b): 十进制整型,十进制无符号整型,十进制长整型,十进制无符号长整型, 八进制整型,十六进制整型。
+- (c): double, float, long double
+- (d): 十进制整型,十进制无符号整型,double, double
+
+## 练习2.6
+下面两组定义是否有区别,如果有,请叙述之:
+```cpp
+int month = 9, day = 7;
+int month = 09, day = 07;
+```
+解:
+
+第一行定义的是十进制的整型,第二行定义的是八进制的整型。但是month变量有误,八进制不能直接写9。
+
+## 练习2.7
+下述字面值表示何种含义?它们各自的数据类型是什么?
+```cpp
+(a) "Who goes with F\145rgus?\012"
+(b) 3.14e1L
+(c) 1024f
+(d) 3.14L
+```
+
+解:
+- (a)是一个字符串,包含两个转义字符,其中\145表示字符e,\012 表示一个换行符,因此该字符串的输出结果是Who goes with Fergus?
+- (b)是一个科学计数法表示的扩展精度浮点数,大小为3.14*10=31.4。
+- (c)试图表示一个单精度浮点数,但是该形式在某些编译器中将报错,因为后缀f直接跟在了整数1024后面;改写成1024f就可以了。
+- (d)是一个扩展精度浮点数,类型是long double,大小为3.14。
+
+## 练习2.8
+请利用转义序列编写一段程序,要求先输出 2M,然后转到新一行。修改程序使其先输出 2,然后输出制表符,再输出 M,最后转到新一行。
+
+解:
+```cpp
+#include
+int main()
+{
+ std::cout << 2 << "\115\012";
+ std::cout << 2 << "\t\115\012";
+ return 0;
+}
+```
+
+## 练习2.9
+解释下列定义的含义,对于非法的定义,请说明错在何处并将其改正。
+
+- (a) std::cin >> int input_value;
+- (b) int i = { 3.14 };
+- (c) double salary = wage = 9999.99;
+- (d) int i = 3.14;
+
+解:
+
+(a): 应该先定义再使用。
+```cpp
+int input_value = 0;
+std::cin >> input_value;
+```
+
+(b): 用列表初始化内置类型的变量时,如果存在丢失信息的风险,则编译器将报错。
+```cpp
+double i = { 3.14 };
+```
+
+(c): 声明多个变量时应该用逗号将变量名隔开,不能用赋值运算符连接。
+```cpp
+double salary,wage;
+double salary = wage = 9999.99;
+```
+
+(d): 不报错,但是小数部分会被截断。
+```cpp
+double i = 3.14;
+```
+
+## 练习2.10
+下列变量的初值分别是什么?
+```cpp
+std::string global_str;
+int global_int;
+int main()
+{
+ int local_int;
+ std::string local_str;
+}
+```
+
+解:
+
+`global_str`和`global_int`是全局变量,所以初值分别为空字符串和0。
+`local_int`是局部变量并且没有初始化,它的初值是未定义的。
+`local_str` 是 `string` 类的对象,它的值由类确定,为空字符串。
+
+## 练习2.11
+指出下面的语句是声明还是定义:
+
+- (a) extern int ix = 1024;
+- (b) int iy;
+- (c) extern int iz;
+
+解:
+声明与定义的关系是:声明使得名字为程序所知,而定义负责创建与名字关联的实体。
+(a): 定义变量ix
+(b): 声明并定义变量iy
+(c): 声明变量iz
+
+## 练习2.12
+请指出下面的名字中哪些是非法的?
+
+- (a) int double = 3.14;
+- (b) int _;
+- (c) int catch-22;
+- (d) int 1_or_2 = 1;
+- (e) double Double = 3.14;
+
+解:
+
+(a)是非法的,因为double是C++关键字,代表一种数据类型,不能作为变量的名字。
+(c)是非法的,在标识符中只能出现字母、数字和下画线,不能出现符号-,如果改成“int catch 22;”就是合法的了。
+(d)是非法的,因为标识符必须以字母或下画线开头,不能以数字开头。(b)和(e)是合法的命名。
+
+## 练习2.13
+下面程序中`j`的值是多少?
+
+```cpp
+int i = 42;
+int main()
+{
+ int i = 100;
+ int j = i;
+}
+```
+
+解:
+
+`j`的值是100,局部变量`i`覆盖了全局变量`i`。
+
+## 练习2.14
+下面的程序合法吗?如果合法,它将输出什么?
+```cpp
+int i = 100, sum = 0;
+for (int i = 0; i != 10; ++i)
+ sum += i;
+std::cout << i << " " << sum << std::endl;
+```
+
+解:
+
+合法。输出是 100 45 。
+
+该程序存在嵌套的作用域,其中for循环之外是外层作用域,for循环内部是内层作用域。首先在外层作用域中定义了i和sum,但是在for循环内部i被重新定义了,因此for循环实际上是从i=0循环到了i=9,内层作用域中没有重新定义 sum,因此sum的初始值是0并在此基础上依次累加。最后一句输出语句位于外层作用域中,此时在for循环内部重新定义的i已经失效,因此实际输出的仍然是外层作用域的i,值为100;而sum经由循环累加,值变为了 45。
+
+## 练习2.15
+下面的哪个定义是不合法的?为什么?
+
+- (a) int ival = 1.01;
+- (b) int &rval1 = 1.01;
+- (c) int &rval2 = ival;
+- (d) int &rval3;
+
+解:
+
+(b)和(d)不合法,(b)引用必须绑定在对象上,(d)引用必须初始化。
+
+## 练习2.16
+考察下面的所有赋值然后回答:哪些赋值是不合法的?为什么?哪些赋值是合法的?它们执行了哪些操作?
+
+```cpp
+int i = 0, &r1 = i;
+double d = 0, &r2 = d;
+```
+- (a) r2 = 3.14159;
+- (b) r2 = r1;
+- (c) i = r2;
+- (d) r1 = d;
+
+解:
+
+- (a): 合法。给 d 赋值为 3.14159。
+- (b): 合法。会执行自动转换(int->double)。
+- (c): 合法。会发生小数截取。
+- (d): 合法。会发生小数截取。
+
+## 练习2.17
+执行下面的代码段将输出什么结果?
+```cpp
+int i, &ri = i;
+i = 5; ri = 10;
+std::cout << i << " " << ri << std::endl;
+```
+
+解:
+
+程序的输出结果是10 10。
+
+引用不是对象,它只是为已经存在的对象起了另外一个名字,因此ri实际上是 i的别名。在上述程序中,首先将i赋值为5,然后把这个值更新为10。因为ri是i的引用,所以它们的输出结果是一样的。
+
+## 练习2.18
+编写代码分别改变指针的值以及指针所指对象的值。
+
+解:
+```cpp
+int main() {
+ int i = 1, j = 2;
+ int *p = &i;
+ std::cout << p << " " << *p << std::endl;
+ p = &j; //更改指针的值,令p指向另一个整数对象j
+ std::cout << p << " " << *p << std::endl;
+ *p = 10; // 显式改变指针p指向的内容
+ std::cout << p << " " << *p << std::endl;
+ j = 20; // 通过改变变量j的值
+ std::cout << p << " " << *p << std::endl;
+ return 0;
+}
+```
+
+输出结果:
+
+```cpp
+0x61fe14 1
+0x61fe10 2
+0x61fe10 10
+0x61fe10 20
+```
+
+## 练习2.19
+说明指针和引用的主要区别
+
+解:
+
+指针“指向”内存中的某个对象,而引用“绑定到”内存中的某个对象,他们都实现了对于娶她对象的间接访问,二者的区别主要有两方面:
+1. 指针本身就是一个对象,允许对指针复制和拷贝,而且在指针的生命周期内它可以指向几个不同的对象;引用不是一个对象,无法令引用重新绑定到另外一个对象。
+2. 指针无须在定义时赋初值,和其他内置类型一样,在块作用域内定义的指针如果没有被初始化,也将拥有一个不确定的值;引用则必须在定义时赋初值。
+
+## 练习2.20
+请叙述下面这段代码的作用。
+
+```cpp
+int i = 42;
+int *p1 = &i;
+*p1 = *p1 * *p1;
+```
+
+解:
+
+这段代码首先定义了一个整数变量 `i` 并设其初值为 `42`;接着定义了一个整型指针 `p1`,令其指向变量 `i`;最后取出 `p1` 所指的当前值,计算平方后重新赋给 `p1` 所指的变量 `i`。
+
+第二行的 `*` 表示声明一个指针,第三行的 `*` 表示解引用运算,即取出指针 `p1` 所指对象的值。
+
+
+## 练习2.21
+请解释下述定义。在这些定义中有非法的吗?如果有,为什么?
+
+`int i = 0;`
+- (a) double* dp = &i;
+- (b) int *ip = i;
+- (c) int *p = &i;
+
+解:
+
+- (a): 非法。不能将一个指向 `double` 的指针指向 `int` 。
+- (b): 非法。不能将 `int` 变量赋给指针。
+- (c): 合法。
+
+## 练习2.22
+假设 p 是一个 int 型指针,请说明下述代码的含义。
+
+```cpp
+if (p) // ...
+if (*p) // ...
+```
+
+解:
+
+指针 p 作为 if 语句的条件时,实际检验的是指针本身的值,即指针所指的地址值。如果指针指向一个真实存在的变量,则其值必不为 0,此时条件为真;如果指针没有指向任何对象或者是无效指针,则对 p 的使用将引发不可预计的结果。
+
+解引用运算符 `*p` 作为 if 语句的条件时,实际检验的是指针所指的对象内容,在上面的示例中是指针p所指的int值。如果该 int 值为0,则条件为假;否则。如果该 int 值不为0,对应条件为真。
+
+
+## 练习2.23
+给定指针 p,你能知道它是否指向了一个合法的对象吗?如果能,叙述判断的思路;如果不能,也请说明原因。
+
+解:
+
+在 C++ 程序中,应该尽量初始化所有指针,并且尽可能等定义了对象之后再定义指向它的指针。如果实在不清楚指针应该指向何处,就把它初始化为 nullptr 或者 0,这样程序就能检测并知道它有没有指向一个具体的对象了。其中,nullptr 是C++11新标准刚刚引入的一个特殊字面值,它可以转换成任意其他的指针类型。在此前提下,判断p是否指向合法的对象,只需把 p 作为 if 语句的条件即可,如果 p 的值是 nullptr,则条件为假;反之,条件为真。
+如果不注意初始化所有指针而贸然判断指针的值,则有可能引发不可预知的结果。一种处理的办法是把if(p)置于try结构中,当程序块顺利执行时,表示p指向了合法的对象:当程序块出错跳转到catch语句时,表示p没有指向合法的对象。
+
+## 练习2.24
+在下面这段代码中为什么 p 合法而 lp 非法?
+
+```cpp
+int i = 42;
+void *p = &i;
+long *lp = &i;
+```
+
+解:
+
+`void *`是一种特殊的指针类型,可用于存放任意对象的地址。
+而其他指针类型必须要与所指对象严格匹配。
+
+## 练习2.25
+说明下列变量的类型和值。
+```cpp
+(a) int* ip, i, &r = i;
+(b) int i, *ip = 0;
+(c) int* ip, ip2;
+```
+
+解:
+- (a): ip 是一个指向 int 的指针, i 是一个 int, r 是 i 的引用。
+- (b): i 是 int , ip 是一个空指针。
+- (c): ip 是一个指向 int 的指针, ip2 是一个 int。
+
+## 练习2.26
+下面哪些语句是合法的?如果不合法,请说明为什么?
+
+解:
+```cpp
+const int buf; // 不合法, const 对象必须初始化
+int cnt = 0; // 合法
+const int sz = cnt; // 合法
+++cnt; ++sz; // 不合法, const 对象不能被改变
+```
+
+## 练习2.27
+下面的哪些初始化是合法的?请说明原因。
+
+解:
+
+```cpp
+int i = -1, &r = 0; // 不合法, 非常用引用 r 不能引用字面值常量 0
+int *const p2 = &i2; // 合法,常量指针
+const int i = -1, &r = 0; // 合法, r 是一个常量引用,可以绑定到字面值常量 0
+const int *const p3 = &i2; // 合法
+const int *p1 = &i2; // 合法
+const int &const r2; // 不合法, r2 是引用,引用本身不是对象,不能让引用恒定不变
+const int i2 = i, &r = i; // 合法,i2是一个常量,r是一个常量引用
+```
+
+## 练习2.28
+说明下面的这些定义是什么意思,挑出其中不合法的。
+
+解:
+```cpp
+int i, *const cp; // 不合法, const 指针必须初始化
+int *p1, *const p2; // 不合法, const 指针必须初始化
+const int ic, &r = ic; // 不合法, const int 必须初始化
+const int *const p3; // 不合法, const 指针必须初始化
+const int *p; // 合法. 一个指针,指向 const int
+```
+
+## 练习2.29
+假设已有上一个练习中定义的那些变量,则下面的哪些语句是合法的?请说明原因。
+
+解:
+```cpp
+i = ic; // 合法, 常量赋值给普通变量
+p1 = p3; // 不合法, p3 是const指针不能赋值给普通指针
+p1 = ⁣ // 不合法, 普通指针不能指向常量
+p3 = ⁣ // 不合法, p3 是一个常量指针,不能被赋值
+p2 = p1; // 不合法, p2 是一个常量指针,不能被赋值
+ic = *p3; // 不合法, ic是常量,不能被赋值
+```
+
+## 练习2.30
+对于下面的这些语句,请说明对象被声明成了顶层const还是底层const?
+
+```cpp
+const int v2 = 0; int v1 = v2;
+int *p1 = &v1, &r1 = v1;
+const int *p2 = &v2, *const p3 = &i, &r2 = v2;
+```
+
+解:
+
+v2 和 p3 是顶层const,分别表示一个整型常量和一个整型常量指针;p2 和 r2 是底层const,分别表示他们所指的对象是常量。
+
+## 练习2.31
+假设已有上一个练习中所做的那些声明,则下面的哪些语句是合法的?请说明顶层const和底层const在每个例子中有何体现。
+
+解:
+
+
+```cpp
+r1 = v2;
+p1 = p2;
+p2 = p1;
+p1 = p3;
+p2 = p3;
+```
+
+在执行拷贝操作时,顶层const和底层const区别明显。其中,顶层 const不受影响,这是因为拷贝操作并不会改变被拷贝对象的值。底层const的限制则不容忽视,拷入和拷出的对象必须具有相同的底层const资格,或者两个对象的数据类型必须能够转换。一般来说,非常量可以转换成常量,反之则不行。
+
+r1=v2;是合法的,r1是一个非常量引用,v2是一个常量(顶层const),把v2的值拷贝给r1不会对v2有任何影响。
+
+pl=p2;是非法的,p1是普通指针,指向的对象可以是任意值,p2是指向常量的指针(底层const),令p1指向p2所指的内容,有可能错误地改变常量的值。
+
+p2=p1;是合法的,与上一条语句相反,p2可以指向一个非常量,只不过我们不会通过p2更改它所指的值。
+
+pl=p3;是非法的,p3包含底层const定义(p3所指的对象是常量),不能把 p3的值赋给普通指针。
+
+p2=p3;是合法的,p2和p3包含相同的底层const,p3的顶层const则可以忽略不计。
+
+
+## 练习2.32
+下面的代码是否合法?如果非法,请设法将其修改正确。
+```cpp
+int null = 0, *p = null;
+```
+解:
+
+上述代码是非法的,null是一个int 变量,p是一个int指针,二者不能直 接绑定。仅从语法角度来说,可以将代码修修改为:
+`int null=0,*p=&null;`
+
+显然,这种改法与代码的原意不一定相符。另一种改法是使用nullptr: `int null=0,*p=nullptri`
+
+## 练习2.33
+利用本节定义的变量,判断下列语句的运行结果。
+
+解:
+
+```cpp
+a=42; // a 是 int
+b=42; // b 是一个 int,(ci的顶层const在拷贝时被忽略掉了)
+c=42; // c 也是一个int
+d=42; // d 是一个 int *,所以语句非法
+e=42; // e 是一个 const int *, 所以语句非法
+g=42; // g 是一个 const int 的引用,引用都是底层const,所以不能被赋值
+```
+【出题思路】
+
+本题旨在考查auto说明符与复合类型、常量混合使用时的各种情形。首先,使用引用其实是使用引用的对象,所以当引用被用作初始值时,真正参与初始化的其实是引用对象的值,编译器以引用对象的类型作为auto的推断类型。其次,auto一般会忽略掉顶层const,同时保留底层const。
+
+【解答】
+前3条赋值语句是合法的,原因如下:
+r是i的别名,而i是一个整数,所以a的类型推断结果是一个整数;ci是一个整型常量,在类型推断时顶层const被忽略掉了,所以b是一个整数;cr是ci的别名,而ci是一个整型常量,所以c的类型推断结果是一个整数。因为a、b、c都是整数,所以为其赋值 42 是合法的。
+后3条赋值语句是非法的,原因如下:
+i是一个整数,&i是i的地址,所以d的类型推断结果是一个整型指针;ci是一个整型常量,&ci是一个整型常量的地址,所以e的类型推断结果是一个指向整型常量的指针:ci是一个整型常量,所以a的类型推断结果是一个整型常量引用。因为d和e都是指针,所以不能直接用字面值常量为其赋值;g绑定到了整型常量,所以不能修改它的值。
+
+
+## 练习2.34
+基于上一个练习中的变量和语句编写一段程序,输出赋值前后变量的内容,你刚才的推断正确吗?如果不对,请反复研读本节的示例直到你明白错在何处为止。
+
+## 练习2.35
+判断下列定义推断出的类型是什么,然后编写程序进行验证。
+
+```cpp
+const int i = 42;
+auto j = i; const auto &k = i; auto *p = &i;
+const auto j2 = i, &k2 = i;
+```
+
+解:
+
+j 是 int,k 是 const int的引用,p 是const int *,j2 是const int,k2 是 const int 的引用。
+
+## 练习2.36
+关于下面的代码,请指出每一个变量的类型以及程序结束时它们各自的值。
+
+```cpp
+int a = 3, b = 4;
+decltype(a) c = a;
+decltype((b)) d = a;
+++c;
+++d;
+```
+
+解:
+
+在本题的程序中,初始情况下a的值是3、b的值是4。decltype(a)c=ai使用的是一个不加括号的变量,因此c的的类型就是a的类型,即该语句等同于`int c=a;`,此时c是一个新整型变量,值为 3.
+`decltype((b)) d=a;`使用的是一个
+加了括号的变量,因此d的类型是引用, 即该语句等同于`int &d=a;`,此时d是变量a的别名。
+执行++c;++d;时,变量c的值自增为4,因为d是a的别名,所以d自增1
+意味着a的值变成了4。当程序结束时, a、b、c、d的值都是 4。
+
+## 练习2.37
+赋值是会产生引用的一类典型表达式,引用的类型就是左值的类型。也就是说,如果 i 是 int,则表达式 i=x 的类型是 int&。根据这一特点,请指出下面的代码中每一个变量的类型和值。
+
+```cpp
+int a = 3, b = 4;
+decltype(a) c = a;
+decltype(a = b) d = a;
+```
+
+解:
+
+c 是 int 类型,值为 3。d 是 int& 类型,绑定到 a。
+
+## 练习2.38
+说明由decltype 指定类型和由auto指定类型有何区别。请举一个例子,decltype指定的类型与auto指定的类型一样;再举一个例子,decltype指定的类型与auto指定的类型不一样。
+
+解:
+
+decltype 处理顶层const和引用的方式与 auto不同,decltype会将顶层const和引用保留起来。
+
+auto和decltype的区别主要有三个方面:
+
+第一,auto类型说明符用编译器计算变量的初始值来推断其类型,而decltype虽然也让编译器分析表达式并得到它的类型,但是不实际计算表达式的值。
+
+第二,编译器推断出来的auto类型有时候和初始值的类型并不完全一样,编译器会适当地改变结果类型使其更符合初女治化规则。例如,auto一般会忽略掉顶层 const,而把底层const保留下来。与之相目反,decltype会保留变量的顶层const。
+
+第三,与auto不同,decltype的结果类型与表达式形式密切相关,如果变量名加上了一对括号,则得到的类型与不加括号时会有不同。如果decltype使用的是一个不加括号的变量,则得到的结果就就是该变量的类型;如果给变量加上了一层或多层括号,则编译器将推断得到引用类型。
+
+```cpp
+#include
+
+
+int main() {
+ int a = 1;
+ auto c1 = a;
+ decltype(a) c2 = a;
+ decltype((a)) c3 = a;
+
+ const int d = 3;
+ auto f1 = d;
+ decltype(d) f2 = d;
+
+ std::cout << typeid(c1).name() << std::endl;
+ std::cout << typeid(c2).name() << std::endl;
+ std::cout << typeid(c3).name() << std::endl;
+ std::cout << typeid(f1).name() << std::endl;
+ std::cout << typeid(f2).name() << std::endl;
+
+ c1++;
+ c2++;
+ c3++;
+ f1++;
+// f2++; f2是整型变量,不能执行自增操作
+
+ std::cout << c1 << " " << c2 << " "<< c3 << " "<< f1 << " "<< f2 << " " << std::endl;
+ return 0;
+}
+
+```
+
+## 练习2.39
+编译下面的程序观察其运行结果,注意,如果忘记写类定义体后面的分号会发生什么情况?记录下相关的信息,以后可能会有用。
+```cpp
+struct Foo { /* 此处为空 */ } // 注意:没有分号
+int main()
+{
+ return 0;
+}。
+```
+解:
+
+提示应输入分号。
+
+## 练习2.40
+根据自己的理解写出 Sales_data 类,最好与书中的例子有所区别。
+
+```cpp
+struct Sale_data
+{
+ std::string bookNo;
+ std::string bookName;
+ unsigned units_sold = 0;
+ double revenue = 0.0;
+ double price = 0.0;
+ //...
+}
+```
+
+## 练习2.41
+使用你自己的Sale_data类重写1.5.1节(第20页)、1.5.2节(第21页)和1.6节(第22页)的练习。眼下先把Sales_data类的定义和main函数放在一个文件里。
+
+```cpp
+// 1.5.1
+
+#include
+#include
+
+struct Sale_data
+{
+ std::string bookNo;
+ unsigned units_sold = 0;
+ double revenue = 0.0;
+};
+
+int main()
+{
+ Sale_data book;
+ double price;
+ std::cin >> book.bookNo >> book.units_sold >> price;
+ book.revenue = book.units_sold * price;
+ std::cout << book.bookNo << " " << book.units_sold << " " << book.revenue << " " << price;
+
+ return 0;
+}
+
+```
+
+```cpp
+// 1.5.2
+
+#include
+#include
+
+struct Sale_data
+{
+ std::string bookNo;
+ unsigned units_sold = 0;
+ double revenue = 0.0;
+};
+
+int main()
+{
+ Sale_data book1, book2;
+ double price1, price2;
+ std::cin >> book1.bookNo >> book1.units_sold >> price1;
+ std::cin >> book2.bookNo >> book2.units_sold >> price2;
+ book1.revenue = book1.units_sold * price1;
+ book2.revenue = book2.units_sold * price2;
+
+ if (book1.bookNo == book2.bookNo)
+ {
+ unsigned totalCnt = book1.units_sold + book2.units_sold;
+ double totalRevenue = book1.revenue + book2.revenue;
+ std::cout << book1.bookNo << " " << totalCnt << " " << totalRevenue << " ";
+ if (totalCnt != 0)
+ std::cout << totalRevenue / totalCnt << std::endl;
+ else
+ std::cout << "(no sales)" << std::endl;
+ return 0;
+ }
+ else
+ {
+ std::cerr << "Data must refer to same ISBN" << std::endl;
+ return -1; // indicate failure
+ }
+}
+
+```
+
+```cpp
+// 1.6
+
+#include
+#include
+
+struct Sale_data
+{
+ std::string bookNo;
+ unsigned units_sold = 0;
+ double revenue = 0.0;
+};
+
+int main()
+{
+ Sale_data total;
+ double totalPrice;
+ if (std::cin >> total.bookNo >> total.units_sold >> totalPrice)
+ {
+ total.revenue = total.units_sold * totalPrice;
+
+ Sale_data trans;
+ double transPrice;
+ while (std::cin >> trans.bookNo >> trans.units_sold >> transPrice)
+ {
+ trans.revenue = trans.units_sold * transPrice;
+
+ if (total.bookNo == trans.bookNo)
+ {
+ total.units_sold += trans.units_sold;
+ total.revenue += trans.revenue;
+ }
+ else
+ {
+ std::cout << total.bookNo << " " << total.units_sold << " " << total.revenue << " ";
+ if (total.units_sold != 0)
+ std::cout << total.revenue / total.units_sold << std::endl;
+ else
+ std::cout << "(no sales)" << std::endl;
+
+ total.bookNo = trans.bookNo;
+ total.units_sold = trans.units_sold;
+ total.revenue = trans.revenue;
+ }
+ }
+
+ std::cout << total.bookNo << " " << total.units_sold << " " << total.revenue << " ";
+ if (total.units_sold != 0)
+ std::cout << total.revenue / total.units_sold << std::endl;
+ else
+ std::cout << "(no sales)" << std::endl;
+
+ return 0;
+ }
+ else
+ {
+ std::cerr << "No data?!" << std::endl;
+ return -1; // indicate failure
+ }
+}
+```
+
+## 练习2.42
+根据你自己的理解重写一个Sales_data.h头文件,并以此为基础重做2.6.2节(第67页)的练习。
+
+
diff --git a/docs/Cpp-Primer-exercise/3.字符串、向量和数组.md b/docs/Cpp-Primer-exercise/3.字符串、向量和数组.md
new file mode 100644
index 0000000..a8ecc03
--- /dev/null
+++ b/docs/Cpp-Primer-exercise/3.字符串、向量和数组.md
@@ -0,0 +1,1360 @@
+---
+sort: 3
+---
+
+# 字符串、向量和数组
+
+
+## 练习3.1
+
+使用恰当的using 声明重做 1.4.1节和2.6.2节的练习。
+
+解:
+
+1.4.1
+
+```cpp
+#include
+using std::cout;
+using std::endl;
+int main() {
+ int i = 50, sum = 0;
+ while (i <= 100) {
+ sum += i;
+ i += 1;
+ }
+ cout << "sum is " << sum << endl;
+}
+```
+
+2.6.2 类似
+
+## 练习3.2
+编写一段程序从标准输入中一次读入一行,然后修改该程序使其一次读入一个词。
+
+解:
+
+一次读入一行:
+
+```cpp
+#include
+#include
+
+using std::string;
+using std::cin;
+using std::cout;
+using std::endl;
+using std::getline;
+
+int main()
+{
+ string s;
+ while (getline(cin,s))
+ {
+ cout << s << endl;
+ }
+ return 0;
+}
+```
+
+一次读入一个词
+
+```cpp
+#include
+#include
+
+using std::string;
+using std::cin;
+using std::cout;
+using std::endl;
+using std::getline;
+
+int main()
+{
+ string s;
+ while (cin >> s)
+ {
+ cout << s << endl;
+ }
+ return 0;
+}
+```
+
+
+## 练习3.3
+请说明string类的输入运算符和getline函数分别是如何处理空白字符的。
+
+解:
+
+- 类似`is >> s`的读取:string对象会忽略开头的空白(空格符、换行符、制表符等)并从第一个真正的字符开始,直到遇见下一**空白**为止。
+- 类似`getline(is, s)`的读取:string对象会从输入流中读取字符,直到遇见**换行符**为止。
+
+
+## 练习3.4
+编写一段程序读取两个字符串,比较其是否相等并输出结果。如果不相等,输出比较大的那个字符串。改写上述程序,比较输入的两个字符串是否等长,如果不等长,输出长度较大的那个字符串。
+
+解:
+
+
+
+```cpp
+#include
+#include
+
+using namespace std;
+
+int main() {
+ string str1, str2;
+ cout << "请输入两个字符串" << endl;
+ cin >> str1;
+ cin >> str2;
+
+ if (str1 == str2) {
+ cout << "两个字符相等" << endl;
+ }else if (str1 > str2) {
+ cout << str1 << endl;
+ }else {
+ cout << str2 < len2)
+ cout << str1 << "比" << str2 << "的长度多" << len1 - len2 << endl;
+ else
+ cout << str1 << "比" << str2 << "的长度少" << len2 - len1 << endl;
+
+
+ return 0;
+}
+
+```
+
+
+## 练习3.5
+编写一段程序从标准输入中读入多个字符串并将他们连接起来,输出连接成的大字符串。然后修改上述程序,用空格把输入的多个字符串分割开来。
+
+解:
+
+未隔开的:
+```cpp
+#include
+#include
+
+using namespace std;
+
+int main() {
+ char cont = 'y';
+ string s, result;
+ cout << "请输入字符串" << endl;
+ while(cin >> s) {
+ result += s;
+ cout << "是否继续(y or n)" << endl;
+ cin >> cont;
+ if (cont == 'y' || cont == 'Y'){
+ cout << "请输入下一个字符串" << endl;
+ }else {
+ break;
+ }
+ }
+ cout << "拼接后的字符为" <
+#include
+
+using std::string;
+using std::cin;
+using std::cout;
+using std::endl;
+
+int main()
+{
+ string result, s;
+ while (cin >> s)
+ {
+ result += s + " ";
+ }
+ cout << result << endl;
+
+ return 0;
+}
+```
+
+## 练习3.6
+编写一段程序,使用范围for语句将字符串内所有字符用X代替。
+
+解:
+
+```cpp
+#include
+#include
+
+using namespace std;
+
+int main() {
+ string s;
+ cout << "请输入一个字符串" <
+#include
+
+using namespace std;
+// while
+int main() {
+ string s;
+ cout << "请输入一个字符串" <
+#include
+
+using namespace std;
+
+int main() {
+ string s;
+ cout << "请输入一个字符串" <
+#include
+
+using namespace std;
+
+int main() {
+ string s;
+ cout << "请输入一个字符串" <> ivec; // 在C++11当中合法
+vector svec = ivec; // 不合法,类型不一样
+vector svec(10, "null"); // 合法
+```
+
+## 练习3.13
+下列的vector对象各包含多少个元素?这些元素的值分别是多少?
+
+```cpp
+vector v1; // size:0, no values.
+vector v2(10); // size:10, value:0
+vector v3(10, 42); // size:10, value:42
+vector v4{ 10 }; // size:1, value:10
+vector v5{ 10, 42 }; // size:2, value:10, 42
+vector v6{ 10 }; // size:10, value:""
+vector v7{ 10, "hi" }; // size:10, value:"hi"
+```
+
+## 练习3.14
+编写一段程序,用cin读入一组整数并把它们存入一个vector对象。
+
+解:
+
+```cpp
+##include
+#include
+#include
+
+using namespace std;
+using std::vector;
+
+int main() {
+ vector v;
+ int i;
+ char cont = 'y';
+ cout << "请输入数字" << endl;
+ while(cin >> i) {
+ v.push_back(i);
+ cout << "是否继续(y or n)" << endl;
+ cin >> cont;
+ if (cont == 'y' || cont == 'Y'){
+ cout << "请输入下一个整数" << endl;
+ }else {
+ break;
+ }
+ }
+ for (auto &m : v)
+ cout << "输入的整数为" <
+#include
+#include
+
+using namespace std;
+using std::vector;
+
+int main() {
+ vector v;
+ string i;
+ char cont = 'y';
+ cout << "请输入字符串" << endl;
+ while(cin >> i) {
+ v.push_back(i);
+ cout << "是否继续(y or n)" << endl;
+ cin >> cont;
+ if (cont == 'y' || cont == 'Y'){
+ cout << "请输入下一个字符串" << endl;
+ }else {
+ break;
+ }
+ }
+ for (auto &m : v)
+ cout << m << " ";
+ cout << endl;
+ return 0;
+}
+```
+
+## 练习3.16
+编写一段程序,把练习3.13中vector对象的容量和具体内容输出出来
+
+解:
+
+```cpp
+#include
+#include
+#include
+
+using namespace std;
+
+int main()
+{
+ vector v1; // size:0, no values.
+ vector v2(10); // size:10, value:0
+ vector v3(10, 42); // size:10, value:42
+ vector v4{ 10 }; // size:1, value:10
+ vector v5{ 10, 42 }; // size:2, value:10, 42
+ vector v6{ 10 }; // size:10, value:""
+ vector v7{ 10, "hi" }; // size:10, value:"hi"
+
+ cout << "v1 size :" << v1.size() << endl;
+ cout << "v2 size :" << v2.size() << endl;
+ cout << "v3 size :" << v3.size() << endl;
+ cout << "v4 size :" << v4.size() << endl;
+ cout << "v5 size :" << v5.size() << endl;
+ cout << "v6 size :" << v6.size() << endl;
+ cout << "v7 size :" << v7.size() << endl;
+
+ cout << "v1 content: ";
+ for (auto i : v1)
+ {
+ cout << i << " , ";
+ }
+ cout << endl;
+
+ cout << "v2 content: ";
+ for (auto i : v2)
+ {
+ cout << i << " , ";
+ }
+ cout << endl;
+
+ cout << "v3 content: ";
+ for (auto i : v3)
+ {
+ cout << i << " , ";
+ }
+ cout << endl;
+
+ cout << "v4 content: ";
+ for (auto i : v4)
+ {
+ cout << i << " , ";
+ }
+ cout << endl;
+
+ cout << "v5 content: ";
+ for (auto i : v5)
+ {
+ cout << i << " , ";
+ }
+ cout << endl;
+
+ cout << "v6 content: ";
+ for (auto i : v6)
+ {
+ cout << i << " , ";
+ }
+ cout << endl;
+
+ cout << "v7 content: ";
+ for (auto i : v7)
+ {
+ cout << i << " , ";
+ }
+ cout << endl;
+ return 0;
+}
+```
+
+## 练习3.17
+从cin读入一组词并把它们存入一个vector对象,然后设法把所有词都改为大写形式。输出改变后的结果,每个词占一行。
+
+解:
+
+```cpp
+#include
+#include
+#include
+#include
+
+using std::cin;
+using std::cout;
+using std::endl;
+using std::vector;
+using std::string;
+
+int main()
+{
+ vector v;
+ string s;
+
+ while (cin >> s)
+ {
+ v.push_back(s);
+ }
+
+ for (auto &str : v)
+ {
+ for (auto &c : str)
+ {
+ c = toupper(c);
+ }
+ }
+
+ for (auto i : v)
+ {
+ cout << i << endl;
+ }
+ return 0;
+}
+```
+
+## 练习3.18
+下面的程序合法吗?如果不合法,你准备如何修改?
+
+```cpp
+vector ivec;
+ivec[0] = 42;
+```
+
+解:
+
+不合法。应改为:
+```cpp
+ivec.push_back(42);
+```
+
+## 练习3.19
+如果想定义一个含有10个元素的vector对象,所有元素的值都是42,请例举三种不同的实现方法,哪种方式更好呢?
+
+如下三种:
+
+```cpp
+vector ivec1(10, 42);
+
+vector ivec2{ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42 };
+
+vector ivec3 = { 42, 42, 42, 42, 42, 42, 42, 42, 42, 42 };
+
+vector ivec4;
+for (int i = 0; i < 10; ++i)
+ ivec4.push_back(42);
+```
+vector ivec5(10);
+for (auto &i : ivec5) {
+ i = 42;
+}
+
+第一种方式简介直观,当对象数量多且取值重复时适合使用;思路4不限制元素的个数,比较灵活。
+
+## 练习3.20
+读入一组整数并把他们存入一个vector对象,将每对相邻整数的和输出出来。改写你的程序,这次要求先输出第一个和最后一个元素的和,接着输出第二个和倒数第二个元素的和,以此类推。
+
+解:
+
+```cpp
+#include
+#include
+#include
+
+using namespace std;
+using std::vector;
+
+int main() {
+ vector v;
+ int w;
+ cout << "请输入数字" << endl;
+ while (cin >> w) {
+ v.push_back(w);
+ }
+ if (v.size() == 0) {
+ cout << "没有任何元素" <
+#include
+#include
+
+using namespace std;
+
+int main()
+{
+ vector v1; // size:0, no values.
+ vector v2(10); // size:10, value:0
+ vector v3(10, 42); // size:10, value:42
+ vector v4{ 10 }; // size:1, value:10
+ vector v5{ 10, 42 }; // size:2, value:10, 42
+ vector v6{ 10 }; // size:10, value:""
+ vector v7{ 10, "hi" }; // size:10, value:"hi"
+
+ cout << "v1 size :" << v1.size() << endl;
+ cout << "v2 size :" << v2.size() << endl;
+ cout << "v3 size :" << v3.size() << endl;
+ cout << "v4 size :" << v4.size() << endl;
+ cout << "v5 size :" << v5.size() << endl;
+ cout << "v6 size :" << v6.size() << endl;
+ cout << "v7 size :" << v7.size() << endl;
+
+ cout << "v1 content: ";
+ if (v1.cbegin() != v1.cend()) {
+ for (auto it = v1.cbegin(); it != v1.cend(); it++) {
+ cout << *it << " , ";
+ }
+ cout << endl;
+ }
+ cout << "v2 content: ";
+ if (v2.cbegin() != v2.cend()) {
+ for (auto it = v2.cbegin(); it != v2.cend(); it++) {
+ cout << *it << " , ";
+ }
+ cout << endl;
+ }
+
+ cout << "v3 content: ";
+ if (v3.cbegin()!= v3.cend()) {
+ for (auto it = v3.cbegin(); it != v3.cend(); it++){
+ cout << *it << " , ";
+ }
+ cout << endl;
+ }
+
+
+
+ cout << "v4 content: ";
+ if (v4.cbegin() != v4.cend()) {
+ for(auto it = v4.cbegin(); it != v4.cend(); it++) {
+ cout << *it << ",";
+ }
+ cout << endl;
+ }
+
+ cout << "v5 content: ";
+ if (v5.cbegin() != v5.cend()) {
+ for(auto it = v5.cbegin(); it != v5.cend(); it++) {
+ cout << *it << ",";
+ }
+ cout << endl;
+ }
+
+
+ cout << "v6 content: ";
+ if (v6.cbegin() != v6.cend()) {
+ for(auto it = v6.cbegin(); it != v6.cend(); it++) {
+ cout << *it << ",";
+ }
+ cout << endl;
+ }
+
+
+ cout << "v7 content: ";
+ if (v7.cbegin() != v7.cend()) {
+ for(auto it = v7.cbegin(); it != v7.cend(); it++) {
+ cout << *it << ",";
+ }
+ cout << endl;
+ }
+
+ return 0;
+}
+```
+
+## 练习3.22
+修改之前那个输出text第一段的程序,首先把text的第一段全部改成大写形式,然后输出它。
+
+解:
+```cpp
+#include
+#include
+#include
+
+using namespace std;
+using std::vector;
+
+int main() {
+ vector v;
+ string w;
+ cout << "请输入一段话" << endl;
+
+ while (getline(cin, w)) { //利用getline读取一句话,直接回车产生一个空串,表示段落结束
+ v.push_back(w);
+ }
+ for(auto it = v.begin(); it != v.end() && !it -> empty();it++) {
+ for (auto it2 = it->begin(); it2 != it->end(); it2++){
+ *it2 = toupper(*it2);
+ }
+ cout << *it << endl;
+ }
+ return 0;
+}
+```
+
+## 练习3.23
+编写一段程序,创建一个含有10个整数的vector对象,然后使用迭代器将所有元素的值都变成原来的两倍。输出vector对象的内容,检验程序是否正确。
+
+解:
+
+```cpp
+#include
+#include
+#include
+
+using namespace std;
+using std::vector;
+
+int main() {
+
+ vector words;
+ for (int i = 0; i < 10; i++) {
+ words.push_back(rand() % 1000);
+ }
+ cout << "随机生成的数字是:" <
+#include
+#include
+#include
+
+using std::cin;
+using std::cout;
+using std::endl;
+using std::vector;
+using std::string;
+
+int main()
+{
+#include
+#include
+#include
+
+using namespace std;
+using std::vector;
+
+int main() {
+ vector v;
+ int w;
+ cout << "请输入数字" << endl;
+ while (cin >> w) {
+ v.push_back(w);
+ }
+ if (v.cbegin() == v.cend()) {
+ cout << "没有任何元素" <
+#include
+#include
+
+using namespace std;
+using std::vector;
+
+int main() {
+ vector v(11);
+ auto it = v.begin();
+ int i;
+ cout << "请输入一组成绩"<> i) {
+ if (i < 101) {
+ ++*(it + i / 10);
+ }
+ }
+ cout << "各分数段的人数分布式" << endl;
+ for (it = v.begin(); it!= v.end();it++) {
+ cout << *it << " ";
+ }
+ cout << endl;
+ return 0;
+}
+```
+
+## 练习3.26
+在100页的二分搜索程序中,为什么用的是 `mid = beg + (end - beg) / 2`, 而非 `mid = (beg + end) / 2 ;` ?
+
+解:
+
+因为两个迭代器相互之间支持的运算只有 `-` ,而没有 `+` ,两个迭代器之间相减的结果代表他们之间的距离;
+但是迭代器和迭代器差值(整数值)之间支持 `+`。
+
+## 练习3.27
+假设`txt_size`是一个无参函数,它的返回值是`int`。请回答下列哪个定义是非法的,为什么?
+```cpp
+unsigned buf_size = 1024;
+(a) int ia[buf_size];
+(b) int ia[4 * 7 - 14];
+(c) int ia[txt_size()];
+(d) char st[11] = "fundamental";
+```
+
+解:
+- (a) 非法。 维度必须是一个常量表达式, `buf_size`是一个普通的无符号数,不是常量
+- (b) 合法。
+- (c) 非法。txt_size() 的值必须要到运行时才能得到,没有被定义为`constexpr`,不能作为数组的维度。
+- (d) 非法。数组的大小应该是12。
+
+## 练习3.28
+下列数组中元素的值是什么?
+```cpp
+string sa[10];
+int ia[10];
+int main() {
+ string sa2[10];
+ int ia2[10];
+}
+```
+
+解:
+
+对于内置类型`int`来说,数组`ia`定义在所有函数体之外,所有元素默认初始化为0,而`ia2`定义在main函数的内部,将不被初始化,如果程序试图拷贝或输出未初始化的变量,将遇到未定义的奇异值。
+
+string类本身接收无参数的初始化方式,所以不论数组定义在函数内还是函数外,都被默认初始化为空串。
+
+## 练习3.29
+相比于vector 来说,数组有哪些缺点,请例举一些。
+
+解:
+
+- 数组与vector的相似之处是都能存放类型相同的对象,且这些对象本身没有名字,需要通过其所在位置访问。
+- 数组与vector的最大不同是,数组的大小固定不变,不能随意向数组中增加额外的元素,虽然在某些情境下运行时性能能较好,但是与vector相比损失了灵活性。
+- 具体来说,数组的维度在定义时已经确定,如果我们想更改数组的长度,只能创建一个更大的新数组,然后把原数组的所有有元素复制到新数组中去。我们也无法像vector那样使用 size函数直接获取数组的维度。如果是字符数组,可以调用strlen函数得到字符串的长度; 如果是其他数组,只能使用 sizeof(array)/sizeof(array[0])的方式计算数组的维度。
+
+## 练习3.30
+指出下面代码中的索引错误。
+
+```cpp
+constexpr size_t array_size = 10;
+int ia[array_size];
+for (size_t ix = 1; ix <= array_size; ++ix)
+ ia[ix] = ix;
+```
+
+解:
+
+当`ix`增长到 10 的时候,`ia[ix]`的下标越界。
+
+## 练习3.31
+编写一段程序,定义一个含有10个int的数组,令每个元素的值就是其下标值。
+
+```cpp
+#include
+#include
+#include
+
+using namespace std;
+using std::vector;
+
+int main() {
+ const int sz = 10;
+ int num[sz];
+ for (int i = 0; i < sz; i++) {
+ num[i] = i;
+ }
+
+ for (auto val : num) {
+ cout << val << " ";
+ }
+ cout << endl;
+ return 0;
+}
+```
+
+## 练习3.32
+将上一题刚刚创建的数组拷贝给另一数组。利用vector重写程序,实现类似的功能。
+
+```cpp
+#include
+#include
+#include
+
+using namespace std;
+using std::vector;
+
+int main() {
+ const int sz = 10;
+ vector num3;
+ vector num4;
+ int num1[sz], num2[sz];
+ for (int i = 0; i < sz; i++) {
+ num1[i] = i;
+ }
+ for (int i = 0; i < sz; i++) {
+ num2[i] = num1[i];
+ }
+ cout << "num2中的值为" << endl;
+ for (auto val : num2) {
+ cout << val << " ";
+ }
+ cout << endl;
+
+ for (int i = 0; i < sz; i++) {
+ num3.push_back(i);
+ }
+ for (int i = 0; i < sz; i++) {
+ num4.push_back(num3[i]);
+ }
+ cout << "num4中的值为" << endl;
+ for (auto val : num4) {
+ cout << val << " ";
+ }
+ cout << endl;
+
+ return 0;
+}
+```
+
+## 练习3.33
+对于104页的程序来说,如果不初始化scores将会发生什么?
+
+解:
+
+该数组会含有未定义的数值,因为scores是定义在函数内部的整型数组,不会执行默认初始化。
+
+## 练习3.34
+假定`p1` 和 `p2` 都指向同一个数组中的元素,则下面程序的功能是什么?什么情况下该程序是非法的?
+```cpp
+p1 += p2 - p1;
+```
+
+解:
+
+将 `p1` 移动到 `p2` 的位置。
+
+如果`p1` 和 `p2`的类型不同,则编译时报错。
+
+## 练习3.35
+编写一段程序,利用指针将数组中的元素置为0。
+
+解:
+
+```cpp
+#include
+#include
+#include
+
+using namespace std;
+using std::vector;
+
+int main() {
+ const int sz = 10;
+ int nums[sz];
+ for (int i = 0; i < sz; i++) {
+ nums[i] = i;
+ }
+
+
+ for (auto ptr = nums; ptr != nums + sz; ++ptr) *ptr = 0;
+// int *p = begin(nums);
+// while (p != end(nums)) {
+// *p = 0;
+// p++;
+// }
+
+ for (auto num : nums) {
+ cout << num << " ";
+ }
+ cout << endl;
+ return 0;
+}
+```
+
+## 练习3.36
+编写一段程序,比较两个数组是否相等。再写一段程序,比较两个vector对象是否相等。
+
+解:
+
+```cpp
+#include
+#include
+#include
+
+using std::begin; using std::end; using std::cout; using std::endl; using std::vector;
+
+// pb point to begin of the array, pe point to end of the array.
+bool compare(int* const pb1, int* const pe1, int* const pb2, int* const pe2)
+{
+ if ((pe1 - pb1) != (pe2 - pb2)) // have different size.
+ return false;
+ else
+ {
+ for (int* i = pb1, *j = pb2; (i != pe1) && (j != pe2); ++i, ++j)
+ if (*i != *j) return false;
+ }
+
+ return true;
+}
+
+int main()
+{
+ int arr1[3] = { 0, 1, 2 };
+ int arr2[3] = { 0, 2, 4 };
+
+ if (compare(begin(arr1), end(arr1), begin(arr2), end(arr2)))
+ cout << "The two arrays are equal." << endl;
+ else
+ cout << "The two arrays are not equal." << endl;
+
+ cout << "==========" << endl;
+
+ vector vec1 = { 0, 1, 2 };
+ vector vec2 = { 0, 1, 2 };
+
+ if (vec1 == vec2)
+ cout << "The two vectors are equal." << endl;
+ else
+ cout << "The two vectors are not equal." << endl;
+
+ return 0;
+}
+```
+
+## 练习3.37
+下面的程序是何含义,程序的输出结果是什么?
+```cpp
+const char ca[] = { 'h', 'e', 'l', 'l', 'o' };
+const char *cp = ca;
+while (*cp) {
+ cout << *cp << endl;
+ ++cp;
+}
+```
+
+解:
+
+会将ca 字符数组中的元素打印出来。但是因为没有空字符的存在,程序不会退出循环。
+
+## 练习3.38
+在本节中我们提到,将两个指针相加不但是非法的,而且也没有什么意义。请问为什么两个指针相加没有意义?
+
+解:
+
+指针的值是它所指对象的内存地址,将两个指针相减可以表示两个指针(在同一数组中)相距的距离,将指针加上一个整数也可以表示移动这个指针到某一位置。但是两个指针相加并没有逻辑上的意义,因此两个指针不能相加。
+
+## 练习3.39
+编写一段程序,比较两个 `string` 对象。再编写一段程序,比较两个C风格字符串的内容。
+
+解:
+
+```cpp
+#include
+#include
+#include
+using std::cout; using std::endl; using std::string;
+
+int main()
+{
+ // use string.
+ string s1("Mooophy"), s2("Pezy");
+ if (s1 == s2)
+ cout << "same string." << endl;
+ else if (s1 > s2)
+ cout << "Mooophy > Pezy" << endl;
+ else
+ cout << "Mooophy < Pezy" << endl;
+
+ cout << "=========" << endl;
+
+ // use C-Style character strings.
+ const char* cs1 = "Wangyue";
+ const char* cs2 = "Pezy";
+ auto result = strcmp(cs1, cs2);
+ if (result == 0)
+ cout << "same string." << endl;
+ else if (result < 0)
+ cout << "Wangyue < Pezy" << endl;
+ else
+ cout << "Wangyue > Pezy" << endl;
+
+ return 0;
+}
+```
+
+## 练习3.40
+编写一段程序,定义两个字符数组并用字符串字面值初始化它们;接着再定义一个字符数组存放前面两个数组连接后的结果。使用`strcpy`和`strcat`把前两个数组的内容拷贝到第三个数组当中。
+
+解:
+
+```cpp
+#include
+#include
+
+const char cstr1[]="Hello";
+const char cstr2[]="world!";
+
+int main()
+{
+ constexpr size_t new_size = strlen(cstr1) + strlen(" ") + strlen(cstr2) +1;
+ char cstr3[new_size];
+
+ strcpy(cstr3, cstr1);
+ strcat(cstr3, " ");
+ strcat(cstr3, cstr2);
+
+ std::cout << cstr3 << std::endl;
+}
+```
+
+## 练习3.41
+编写一段程序,用整型数组初始化一个vector对象。
+
+```cpp
+#include
+#include
+using std::vector; using std::cout; using std::endl; using std::begin; using std::end;
+
+int main()
+{
+ int arr[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+ vector v(begin(arr), end(arr));
+
+ for (auto i : v) cout << i << " ";
+ cout << endl;
+
+ return 0;
+}
+```
+
+## 练习3.42
+编写一段程序,将含有整数元素的 `vector` 对象拷贝给一个整型数组。
+
+解:
+
+```cpp
+#include
+#include
+
+using namespace std;
+
+int main()
+{
+
+
+ const int sz = 10;
+ vector words;
+ int nums[10];
+
+ for (int i = 0; i < sz; i++) {
+ words.push_back(i);
+ }
+
+ auto it = words.cbegin();
+ for (auto & val : nums) {
+ val = *it;
+ cout << val << " ";
+ it++;
+ }
+ cout << endl;
+
+
+ return 0;
+}
+```
+
+## 练习3.43
+编写3个不同版本的程序,令其均能输出`ia`的元素。
+版本1使用范围`for`语句管理迭代过程;版本2和版本3都使用普通`for`语句,其中版本2要求使用下标运算符,版本3要求使用指针。
+此外,在所有3个版本的程序中都要直接写出数据类型,而不能使用类型别名、`auto`关键字和`decltype`关键字。
+
+解:
+
+```cpp
+#include
+#include
+
+using namespace std;
+
+int main()
+{
+ // 范围for语句
+ int ia[2][2] = {1, 2, 3, 4};
+ for (int (&row)[2] : ia){
+ for(int &val : row) {
+ cout << val << " ";
+ }
+ cout << endl;
+ }
+ // 普通for语句,下标形式
+ for (int i = 0; i != 2; i++){
+ for(int j = 0; j != 2; j++) {
+ cout << ia[i][j] << " ";
+ }
+ cout << endl;
+ }
+ // 普通for语句,指针形式
+ for (int (*p)[2] = ia; p != ia + 2; p++){
+ for(int *q = *p; q != *p + 2; q++) {
+ cout << *q << " ";
+ }
+ cout << endl;
+ }
+ return 0;
+}
+```
+
+## 练习3.44
+改写上一个练习中的程序,使用类型别名来代替循环控制变量的类型。
+
+解:
+
+```cpp
+#include
+#include
+
+using namespace std;
+using int_array = int[2];
+
+int main()
+{
+ // 范围for语句
+ int ia[2][2] = {1, 2, 3, 4};
+ for (int_array &row : ia){
+ for(int &val : row) {
+ cout << val << " ";
+ }
+ cout << endl;
+ }
+ // 普通for语句,下标形式
+ for (int i = 0; i != 2; i++){
+ for(int j = 0; j != 2; j++) {
+ cout << ia[i][j] << " ";
+ }
+ cout << endl;
+ }
+ // 普通for语句,指针形式
+ for (int_array *p = ia; p != ia + 2; p++){
+ for(int *q = *p; q != *p + 2; q++) {
+ cout << *q << " ";
+ }
+ cout << endl;
+ }
+ return 0;
+}
+```
+
+## 练习3.45
+再一次改写程序,这次使用 `auto` 关键字。
+
+解:
+
+```cpp
+#include
+#include
+
+using namespace std;
+using int_array = int[2];
+
+int main()
+{
+ // 范围for语句
+ int ia[2][2] = {1, 2, 3, 4};
+ for (auto &row : ia){
+ for(auto &val : row) {
+ cout << val << " ";
+ }
+ cout << endl;
+ }
+ // 普通for语句,下标形式
+ for (auto i = 0; i != 2; i++){
+ for(auto j = 0; j != 2; j++) {
+ cout << ia[i][j] << " ";
+ }
+ cout << endl;
+ }
+ // 普通for语句,指针形式
+ for (auto *p = ia; p != ia + 2; p++){
+ for(auto *q = *p; q != *p + 2; q++) {
+ cout << *q << " ";
+ }
+ cout << endl;
+ }
+ return 0;
+}
+```
\ No newline at end of file
diff --git a/docs/Cpp-Primer-exercise/4.表达式.md b/docs/Cpp-Primer-exercise/4.表达式.md
new file mode 100644
index 0000000..ae923c7
--- /dev/null
+++ b/docs/Cpp-Primer-exercise/4.表达式.md
@@ -0,0 +1,637 @@
+---
+sort: 4
+---
+
+
+# 表达式
+
+## 练习4.1
+
+表达式`5 + 10 * 20 / 2`的求值结果是多少?
+
+解:
+
+等价于`5 + ((10 * 20) / 2) = 105`
+
+## 练习4.2
+
+根据4.12节中的表,在下述表达式的合理位置添加括号,使得添加括号后运算对象的组合顺序与添加括号前一致。
+(a) `*vec.begin()`
+(b) `*vec.begin() + 1`
+
+解:
+
+```cpp
+*(vec.begin())
+(*(vec.begin())) + 1
+```
+
+## 练习4.3
+
+C++语言没有明确规定大多数二元运算符的求值顺序,给编译器优化留下了余地。这种策略实际上是在代码生成效率和程序潜在缺陷之间进行了权衡,你认为这可以接受吗?请说出你的理由。
+
+解:
+
+正如题目所说,C++只规定了非常少的二元运算符(逻辑与运算符、逻辑或运算符、逗号运算符)的求值顺序,其他绝大多数二元运算符的求值顺序并没有明确规定。这样做提高了代码生成的效率,但是可能引发潜在的缺陷。
+
+关键是缺陷的风险有多大?我们知道,对于没有指定执行顺序的运算符来说,如果表达式指向并修改了同一个对象,将会引发错误并产生未定义的行为;而如果运算对象彼此无关,它们既不会改变同一对象的状态也不执行IO任务,则函数的调用顺序不受限制。
+
+就作者的观点而言,这样的做法在一定程度上是可以接受的,前提是在编写程序时注意以下两点:一是拿不准的时候最好用括号来强制让表达式的组合关系符合程序逻辑的要求;二是一旦改变了某个运算对象的值,在表达式的其地方就不要再使用这个运算对象了。
+
+## 练习4.4
+在下面的表达式中添加括号,说明其求值过程及最终结果。编写程序编译该(不加括号的)表达式并输出结果验证之前的推断。
+
+`12 / 3 * 4 + 5 * 15 + 24 % 4 / 2`
+
+解:
+
+`((12 / 3) * 4) + (5 * 15) + ((24 % 4) / 2) = 16 + 75 + 0 = 91`
+
+## 练习4.5
+
+写出下列表达式的求值结果。
+```cpp
+-30 * 3 + 21 / 5 // -90+4 = -86
+-30 + 3 * 21 / 5 // -30+63/5 = -30+12 = -18
+30 / 3 * 21 % 5 // 10*21%5 = 210%5 = 0
+-30 / 3 * 21 % 4 // -10*21%4 = -210%4 = -2
+```
+
+## 练习4.6
+
+写出一条表达式用于确定一个整数是奇数还是偶数。
+
+解:
+
+```cpp
+if (i % 2 == 0) /* ... */
+```
+或者
+```cpp
+if (i & 0x1) /* ... */
+```
+
+## 练习4.7
+
+溢出是何含义?写出三条将导致溢出的表达式。
+
+解:
+
+当计算的结果超出该类型所能表示的范围时就会产生溢出。
+
+```cpp
+short svalue = 32767; ++svalue; // -32768
+unsigned uivalue = 0; --uivalue; // 4294967295
+unsigned short usvalue = 65535; ++usvalue; // 0
+```
+
+## 练习4.8
+
+说明在逻辑与、逻辑或及相等性运算符中运算对象的求值顺序。
+
+解:
+
+对于逻辑与运算符来说,当且仅当两两个运算对象都为真时结果为真;对于逻辑或运算符来说,只要两个运算对象中的1个为真结果就为真。
+
+逻辑与运算符和逻辑或运算符都是先先求左侧运算对象的值再求右侧运算对象的值,当且仅当左侧运算对象无法确定表过达式的结果时才会计算右侧运算对象的值。这种策略就是短路求值。其策略是:对于干逻辑与运算符来说,当且仅当左侧运算对象为真时才计算右侧运算对象;对于逻转辑或运算符来说,当且仅当左侧运算对象为假时才计算右侧运算对象。
+
+## 练习4.9
+
+解释在下面的`if`语句中条件部分的判断过程。
+```cpp
+const char *cp = "Hello World";
+if (cp && *cp)
+```
+
+解:
+
+cp是指向字符串的指针,因此上式的条件部分含义是首先检查指针cp是否有效。如果cp为空指针或无效指针,则条件不满足。如果cp有效,即cp指向了内存中的某个有效地址,继续解引用指针cp并检查cp所指的对象是否为空字符'\0',如果cp所指的对象不是空字符则条件满足; 否则不满足。
+
+在本例中,显然初始状态下cp指向了字符串的首字符,是有效的;同时当前cp所指的对象是字符'H,不是空字符,所以 if的条件部分为真。
+
+## 练习4.10
+
+为`while`循环写一个条件,使其从标准输入中读取整数,遇到`42`时停止。
+
+解:
+
+```cpp
+int i;
+while(cin >> i && i != 42)
+```
+
+## 练习4.11
+
+书写一条表达式用于测试4个值a、b、c、d的关系,确保a大于b、b大于c、c大于d。
+
+解:
+
+```cpp
+a>b && b>c && c>d
+```
+
+## 练习4.12
+
+假设`i`、`j`和`k`是三个整数,说明表达式`i != j < k`的含义。
+
+解:
+
+这个表达式等于`i != (j < k)`。首先得到`j < k`的结果为`true`或`false`,转换为整数值是`1`或`0`,然后判断`i`不等于`1`或`0` ,最终的结果为`bool`值。
+
+## 练习4.13
+
+在下述语句中,当赋值完成后 i 和 d 的值分别是多少?
+
+```cpp
+int i; double d;
+d = i = 3.5; // i = 3, d = 3.0
+i = d = 3.5; // d = 3.5, i = 3
+```
+
+## 练习4.14
+
+执行下述 if 语句后将发生什么情况?
+
+```cpp
+if (42 = i) // 编译错误。赋值运算符左侧必须是一个可修改的左值。而字面值是右值。
+if (i = 42) // 所有非0整数转换成布尔值时都对应true,该条件恒为真。
+```
+
+## 练习4.15
+
+下面的赋值是非法的,为什么?应该如何修改?
+
+```cpp
+double dval; int ival; int *pi;
+dval = ival = pi = 0;
+```
+
+解:
+该赋值语句是非法的,虽然连续赋值的的形式本身并没有错,但是参与赋值的几个变量类型不同。其中,dval是双精度浮点数,ival是整数,pi是整型指针。
+
+自右向左分析赋值操作的含义,pi=0天表示pi是一个空指针,接下来ival=pi试图把整型指针的值赋给整数,这是不符合合语法规范的操作,无法编译通过。稍作调整,就可以把上述程序改为合法。
+
+```cpp
+dval = ival = 0;
+pi = 0;
+```
+
+## 练习4.16
+
+尽管下面的语句合法,但它们实际执行的行为可能和预期并不一样,为什么?应该如何修改?
+
+```cpp
+if (p = getPtr() != 0)
+if (i = 1024)
+```
+
+解:
+
+(a)的原意是把getPtr()得到的指针赋值给p,然后判断p是否是一个空指针,但上述表达式的实际执行结果与之相距甚这元。因为赋值运算符的优先级低于不相等运算符,所以真正的表达式求值过程是先判到断getPtr()的返回值是否为空指针,如果是则p=0,否则p=1,最后以p的值作为if语句的条件。要想符合原意,应该修改为:
+
+```cpp
+if ((p=getPtr()) != 0)
+if (i == 1024)
+```
+
+## 练习4.17
+
+说明前置递增运算符和后置递增运算符的区别。
+
+解:
+
+递增和递减运算符有两种形式:前置版本和后置版本。前置版本首先将运算对象加1(或减 1),然后把改变后的对象作为求值结果。后置版本也将运算对象加(或减1),但是求值结果是运算对象改变之前那个值的副本。这两种运算符必须作用于左值运算对象。前置版本将对象本身作为左值返回,后置版本则将对象原始值的副本作为右值返回。
+
+我们的建议是,除非必须,否则不用递增(递减)运算符的后置版本。前置版本的递增运算符避免了不必要的工作,它把值加 1 后直接返回改变了的运算对象。与之相比,后置版本需要将原始值存储下来以便于返回这个未修改的内容。如果我们不需要修改之前的值,那么后置版本的操作就是一种浪费。
+
+对于整数和指针类型来说,编译器可能对这种额外的工作进行了一定的优化;
+但是对于相对复杂的迭代器类型来说,这种额外的工作就消耗巨大了。建议养成使用前置版本的习惯,这样不仅不需要担心性能问题,而且更重要的是写出的代码会更符合编程人员的初衷。
+
+## 练习4.18
+
+如果132页那个输出`vector`对象元素的`while`循环使用前置递增运算符,将得到什么结果?
+
+解:
+
+【出题思路】
+
+前置递增运算符先将运算对象加1, 然后把改变后的对象作为求值结果;后置递增运算符也将运算对象加1,但是求值结果是运算对象改变之前那个值的副本。
+
+简言之,如果在一条表达式中出现了 递增运算符,则其计算规律是:++在前, 先加1,后参与运算;++在后,先参与运 算,后加1。
+
+【解答】
+
+基于上述分析,本题不应该把whil e循环的后置递增运算符改为前置递增运算符。如果这样做了,会产生两个错误结果:一是无法输出vector对象的第一个元素;二是当所有元素都不为负时,移动至到最后一个元素的地方,程序试图继续向前移动迭代器并解引用一个根本不存在的元素。
+
+## 练习4.19
+
+假设`ptr`的类型是指向`int`的指针、`vec`的类型是`vector`、`ival`的类型是`int`,说明下面的表达式是何含义?如果有表达式不正确,为什么?应该如何修改?
+
+```cpp
+(a) ptr != 0 && *ptr++
+(b) ival++ && ival
+(c) vec[ival++] <= vec[ival]
+```
+
+解:
+
+- (a)的含义是先判定指针ptr是否为空, 如果不为空,继续判断指针ptr所指的整数是否为非0数。如果非0,则该表达式的们最终求值结果为真;否则为假。最后把指针ptr向后移动一位。该表达式从语法上分析是合法的,但是最后的指针移位操作不一定有意义。如果ptr所指的是整型数组中的某个元素,则ptr可以按照预期移动到下一个元素。如果ptr所指的只是一个独立的整数变量,则移动指针操作将产生未定义的结果。
+- (b)的含义是先检查`ival`的值是否非0,如果非0继续检查`(ival+1)`的值是否非0。只有当两个值都是非0值时,表达式的求值结果为真;否则为假。在4.1.3 节中我们学习到,如果二元运算符的两个运算对象涉及同一个对象并改变对象的值,则这是一种不好的程序写法,应该改写。所以按照程序的原意,本式应该改写成`ival&&(ival +1)`。
+- (c)的含义是比较`vec[ivall和vec[ival+1]`的大小,如果前者较小则求值结果为真,否则为假。与(b)式一样,本式也出现了二元运算符的两个运算对象涉及同一个对象并改变对象值的情况,应该改写为`vec[ivall <=vec[ival +11`。
+
+## 练习4.20
+
+假设`iter`的类型是`vector::iterator`, 说明下面的表达式是否合法。如果合法,表达式的含义是什么?如果不合法,错在何处?
+
+```cpp
+(a) *iter++;
+(b) (*iter)++;
+(c) *iter.empty();
+(d) iter->empty();
+(e) ++*iter;
+(f) iter++->empty();
+```
+
+解:
+
+- (a)是合法的,后置递增运算符的优先级高于解引用运算符,其含义是解引用当前迭代器所处位置的对象内容,然后把迭代器的位置向后移动一位。
+- (b)是非法的,解引用iter得到vector对象当前的元素,结果是一个string,显然string没有后置递增操作。
+- (c)是非法的,解引用运算符的优先级低于点运算符,所以该式先计算 iter.empty(),而迭代器并没有定义empty函数,所以无法通过编译。
+- (d)是合法的,iter->empty(();等价于(*iter).empty();。解引用迭代器得到迭代器当前所指的元素,结果是一个string,显然字符串可以判断是否为空, empty函数在此处有效。
+- (e)是非法的,该式先解引用iter,得到迭代器当前所指的元素,结果是一个 string,显然string没有后置递增操作。
+- (f)是合法的,iter++->empty();等价于(*iter++).empty();。含义是解引用迭代器当前位置的对象内容,得到一个字符串,判断该字符串是否为空,然后把迭代器向后移动一位。
+
+## 练习4.21
+
+编写一段程序,使用条件运算符从`vector`中找到哪些元素的值是奇数,然后将这些奇数值翻倍。
+
+解:
+
+```cpp
+#include
+#include
+#include
+#include
+
+using namespace std;
+
+int main() {
+ vector vInt;
+ const int sz = 10;
+ srand((unsigned ) time(NULL));
+
+ cout <<"数组的初始值为" << endl;
+
+ for(int i = 0; i != sz; ++i) {
+ vInt.push_back(rand() % 100);
+ cout << vInt[i] << " ";
+ }
+ cout <
+#include
+#include
+#include
+
+using namespace std;
+
+int main() {
+
+ string finalGrade;
+ int grade;
+
+ cout <<"请输入成绩" << endl;
+
+ while (cin >> grade && grade >=0 && grade <=100) {
+ finalGrade = (grade >90) ? "high pass": (grade > 75)? "pass": (grade > 60) ? "low pass": "fail";
+ }
+ cout << "等级为" << finalGrade << endl;
+
+ // -------------------------
+ if (grade > 90) cout << "high pass";
+ else if (grade < 60) cout << "fail";
+ else if (grade < 75) cout << "low pass";
+ else cout << "pass";
+ cout << endl;
+
+ return 0;
+}
+
+```
+第二个版本容易理解。当条件运算符嵌套层数变多之后,代码的可读性急剧下降。而`if else`的逻辑很清晰。
+
+## 练习4.23
+
+因为运算符的优先级问题,下面这条表达式无法通过编译。根据4.12节中的表指出它的问题在哪里?应该如何修改?
+
+```cpp
+string s = "word";
+string pl = s + s[s.size() - 1] == 's' ? "" : "s" ;
+```
+
+解:
+
+加法运算符的优先级高于条件运算符。因此要改为:
+
+```cpp
+string pl = s + (s[s.size() - 1] == 's' ? "" : "s") ;
+```
+
+## 练习4.24
+
+本节的示例程序将成绩划分为`high pass`、`pass`、和`fail`三种,它的依据是条件运算符满足右结合律。假如条件运算符满足的是左结合律,求值的过程将是怎样的?
+
+解:
+
+如果条件运算符满足的是左结合律。那么
+
+`finalgrade = (grade > 90) ? "high pass" : (grade < 60) ? "fail" : "pass";`
+等同于
+`finalgrade = ((grade > 90) ? "high pass" : (grade < 60)) ? "fail" : "pass";`
+假如此时 `grade > 90` ,第一个条件表达式的结果是 `"high pass"` ,而字符串字面值的类型是 `const char *`,非空所以为真。因此第二个条件表达式的结果是 `"fail"`。这样就出现了自相矛盾的逻辑。
+
+## 练习4.25
+
+如果一台机器上`int`占32位、`char`占8位,用的是`Latin-1`字符集,其中字符`'q'` 的二进制形式是`01110001`,那么表达式`~'q' << 6`的值是什么?
+
+解:
+
+首先将`char`类型提升为`int`类型,即`00000000 00000000 00000000 01110001`,然后取反,再左移6位,结果是-7296。
+
+## 练习4.26
+
+在本节关于测验成绩的例子中,如果使用`unsigned int` 作为`quiz1` 的类型会发生什么情况?
+
+解:
+
+在有的机器上,`unsigned int` 类型可能只有 16 位,因此结果是未定义的。
+
+## 练习4.27
+
+下列表达式的结果是什么?
+```cpp
+unsigned long ul1 = 3, ul2 = 7;
+(a) ul1 & ul2
+(b) ul1 | ul2
+(c) ul1 && ul2
+(d) ul1 || ul2
+```
+
+解:
+
+- (a) 3
+- (b) 7
+- (c) true
+- (d) ture
+
+## 练习4.28
+
+编写一段程序,输出每一种内置类型所占空间的大小。
+
+解:
+
+```cpp
+#include
+
+using namespace std;
+
+int main()
+{
+ cout << "bool:\t\t" << sizeof(bool) << " bytes" << endl << endl;
+
+ cout << "char:\t\t" << sizeof(char) << " bytes" << endl;
+ cout << "wchar_t:\t" << sizeof(wchar_t) << " bytes" << endl;
+ cout << "char16_t:\t" << sizeof(char16_t) << " bytes" << endl;
+ cout << "char32_t:\t" << sizeof(char32_t) << " bytes" << endl << endl;
+
+ cout << "short:\t\t" << sizeof(short) << " bytes" << endl;
+ cout << "int:\t\t" << sizeof(int) << " bytes" << endl;
+ cout << "long:\t\t" << sizeof(long) << " bytes" << endl;
+ cout << "long long:\t" << sizeof(long long) << " bytes" << endl << endl;
+
+ cout << "float:\t\t" << sizeof(float) << " bytes" << endl;
+ cout << "double:\t\t" << sizeof(double) << " bytes" << endl;
+ cout << "long double:\t" << sizeof(long double) << " bytes" << endl << endl;
+
+ return 0;
+}
+```
+
+输出:
+
+```
+bool: 1 bytes
+
+char: 1 bytes
+wchar_t: 4 bytes
+char16_t: 2 bytes
+char32_t: 4 bytes
+
+short: 2 bytes
+int: 4 bytes
+long: 8 bytes
+long long: 8 bytes
+
+float: 4 bytes
+double: 8 bytes
+long double: 16 bytes
+```
+
+## 练习4.29
+
+推断下面代码的输出结果并说明理由。实际运行这段程序,结果和你想象的一样吗?如不一样,为什么?
+
+```cpp
+int x[10]; int *p = x;
+cout << sizeof(x)/sizeof(*x) << endl;
+cout << sizeof(p)/sizeof(*p) << endl;
+```
+
+解:
+
+sizeof(x)的运算对象x是数组的名字,求值结果是整个数组所占空间的大小,等价于对数组中所有的元素各执行一次sizeof运算并对所得结果求和。读者尤其需要注意,sizeof运算符不会把数组转换成指针来处理。在本例中,x是一个int数组且包含10个元素,所以sizeof(x)的求值结果是10个int值所占的内存空间总和。
+sizeof(*x)的运算对象*x是一条解引用表达式,此处的x既是数组的名称,也表示指向数组首元素的指针,解引用该指针得到指针所指的内容,在本例中是一个int.所以sizeof(*x)在这里等价于sizeof(int),即int所占的内存空间。
+sizeof(x)/sizeof(*x)可以理解为数组x所占的全部空间除以其中一个元素所占的空间,得到的结果应该是数组x的元素总数。实际上,因为C++的内置数组并没有定义成员函数size0),所以通常无法直接得到数组的容量。本题所示的方法是计算得到数组容量的一种常规方法。
+sizeof(p)的运算对象p是一个指针,求值结果是指针所占的空间大小。
+sizeof(*p)的运算对象*p是指针p所指的对象,即int变量x,所以求值结果是int值所占的空间大小。
+在作者的编译环境中,int占4字节,指针也占4字节,所以本题程序的输出结果是:
+10
+1
+
+## 练习4.30
+
+根据4.12节中的表,在下述表达式的适当位置加上括号,使得加上括号之后的表达式的含义与原来的含义相同。
+
+```cpp
+(a) sizeof x + y
+(b) sizeof p->mem[i]
+(c) sizeof a < b
+(d) sizeof f()
+```
+
+解:
+
+(a)的含义是先求变量x所占空间的大小,然后与变量y的值相加;因为sizeof运算符的优先级高于加法运算符的优先级,所以如果想求表达式x+y所占的内存空间,应该改为`sizeof(x+y)`。
+
+(b)的含义是先定位到指针p所指的对象,然后求该对象中名为mem的数组成员第i个元素的尺寸。因为成员选择运算符的优先级高于sizeof的优先级,所以本例无须添加括号。
+
+(c)的含义是先求变量a在内存中所占空间的大小,再把求得的值与变量b的值比较。因为sizeof运算符的优先级高于关系运算符的优先级,所以如果想求表达式`a(d);
+```
+
+## 练习4.37
+
+练习4.37
+用命名的强制类型转换改写下列旧式的转换语句。
+
+```cpp
+int i; double d; const string *ps; char *pc; void *pv;
+(a) pv = (void*)ps;
+(b) i = int(*pc);
+(c) pv = &d;
+(d) pc = (char*)pv;
+```
+
+解:
+
+```cpp
+(a) pv = static_cast(const_cast(ps));
+(b) i = static_cast(*pc);
+(c) pv = static_cast(&d);
+(d) pc = static_cast(pv);
+```
+
+## 练习4.38
+
+说明下面这条表达式的含义。
+
+```cpp
+double slope = static_cast(j/i);
+```
+
+解:
+
+将`j/i`的结果值转换为`double`,然后赋值给`slope`。
+
diff --git a/docs/Cpp-Primer-exercise/5.语句.md b/docs/Cpp-Primer-exercise/5.语句.md
new file mode 100644
index 0000000..1444d9d
--- /dev/null
+++ b/docs/Cpp-Primer-exercise/5.语句.md
@@ -0,0 +1,854 @@
+---
+sort: 5
+---
+
+# 语句
+
+## 练习5.1
+
+什么是空语句?什么时候会用到空语句?
+
+解:
+
+空语句是最简单的语句,空语句由一个单独的分号构成。如果在程序的某个地方,语法上需要一条语句但是逻辑上不需要,此时应该使用空语句,空语句什么也不做。
+
+一种常见的情况是,当循环的全部工作在条件部分就可以完成时,我们通常会用到空语句。使用空语句时最好加上注释,从而令代码的阅读者知道这条语句是有意省略内容的。
+
+```cpp
+while (cin >> s && s != sought)
+ ;
+```
+
+## 练习5.2
+
+什么是块?什么时候会用到块?
+
+解:
+
+块是指用花括号括起来的语句和声明的序列,也称为复合语句。一个块就是一个作用域,在块中引入的名字只能在块内部以及嵌套在块中的子块里访问。如果在
+
+程序的某个地方,语法上需要一条语句, 但是逻辑上需要多条语句,此时应该使用 块。块不需要以分号结束。
+
+例如,循环体必须是一条语句,但是是我们通常需要在循环体内做很多事情,此时就应该把多条语句用花括号括起来,从而把语句序列转变成块。
+
+```cpp
+{
+ // ...
+}
+```
+如果在程序的某个地方,语法上需要一条语句,而逻辑上需要多条语句,此时应该使用块
+```cpp
+while (val <= 10) {
+ sum += val;
+ ++val;
+}
+```
+
+## 练习5.3
+使用逗号运算符重写1.4.1节的`while`循环,使它不再需要块,观察改写之后的代码可读性提高了还是降低了。
+
+```cpp
+while (val <= 10)
+ sum += val, ++val;
+```
+代码的可读性反而降低了。
+
+## 练习5.4
+
+说明下列例子的含义,如果存在问题,试着修改它。
+
+```cpp
+(a) while (string::iterator iter != s.end()) { /* . . . */ }
+
+(b) while (bool status = find(word)) { /* . . . */ }
+ if (!status) { /* . . . */ }
+```
+
+解:
+
+- (a) 这个循环试图用迭代器遍历`string`,但是变量的定义应该放在循环的外面,目前每次循环都会重新定义一个变量,明显是错误的。
+- (b) 这个循环的`while`和`if`是两个独立的语句,`if`语句中无法访问`status`变量,正确的做法是应该将status定义在`while`之前。
+
+## 练习5.5
+写一段自己的程序,使用`if else`语句实现把数字转换为字母成绩的要求。
+
+```cpp
+#include
+#include
+#include
+using namespace std;
+
+int main()
+{
+ int g;
+
+ cout <<"请输入成绩:"<> g;
+
+ if (g < 0 || g > 100) {
+ cout << "该成绩不合法" < 7) {
+ level = "+";
+ }else if (j < 3) {
+ level = "-";
+ }else {
+ level = "";
+ }
+ finalGrade = score + level;
+
+ cout << "等级成绩是:" << finalGrade << endl;
+ return 0;
+}
+```
+
+## 练习5.6
+
+改写上一题的程序,使用条件运算符代替`if else`语句。
+
+```cpp
+#include
+#include
+#include
+using namespace std;
+
+int main()
+{
+
+ int g;
+
+ cout <<"请输入成绩:"<> g;
+
+ if (g < 0 || g > 100) {
+ cout << "该成绩不合法" < 7) ? "+" : (j < 3)? "-": "";
+
+ finalGrade = score + level;
+
+ cout << "等级成绩是:" << finalGrade << endl;
+ return 0;
+}
+```
+
+## 练习5.7
+改写下列代码段中的错误。
+
+```cpp
+(a) if (ival1 != ival2)
+ ival1 = ival2
+ else
+ ival1 = ival2 = 0;
+(b) if (ival < minval)
+ minval = ival;
+ occurs = 1;
+(c) if (int ival = get_value())
+ cout << "ival = " << ival << endl;
+ if (!ival)
+ cout << "ival = 0\n";
+(d) if (ival = 0)
+ ival = get_value();
+```
+
+解:
+
+- (a) `ival1 = ival2` 后面少了分号。
+- (b) 应该用花括号括起来。
+- (c) ival是定义在if语句中的变量,应定义在两个if语句的外部。
+- (d) `if (ival = 0)` 应该改为 `if (ival == 0)`。
+
+## 练习5.8
+什么是“悬垂else”?C++语言是如何处理else子句的?
+
+解:
+
+用来描述在嵌套的`if else`语句中,如果`if`比`else`多时如何处理的问题。C++使用的方法是`else`匹配最近没有配对的`if`。
+
+## 练习5.9
+编写一段程序,使用一系列`if`语句统计从`cin`读入的文本中有多少元音字母。
+
+解:
+
+```cpp
+#include
+
+using namespace std;
+
+int main()
+{
+ unsigned aCnt = 0, eCnt = 0, iCnt = 0, oCnt = 0, uCnt = 0;
+ char ch;
+ cout << "请输入一段文本" << endl;
+ while (cin >> ch)
+ {
+ if (ch == 'a') ++aCnt;
+ else if (ch == 'e') ++eCnt;
+ else if (ch == 'i') ++iCnt;
+ else if (ch == 'o') ++oCnt;
+ else if (ch == 'u') ++uCnt;
+ }
+ cout << "Number of vowel a: \t" << aCnt << '\n'
+ << "Number of vowel e: \t" << eCnt << '\n'
+ << "Number of vowel i: \t" << iCnt << '\n'
+ << "Number of vowel o: \t" << oCnt << '\n'
+ << "Number of vowel u: \t" << uCnt << '\n'
+ << "Total number: \t" << aCnt + eCnt + iCnt + oCnt +uCnt<< endl;
+
+ return 0;
+}
+```
+
+## 练习5.10
+我们之前实现的统计元音字母的程序存在一个问题:如果元音字母以大写形式出现,不会被统计在内。编写一段程序,既统计元音字母的小写形式,也统计元音字母的大写形式,也就是说,新程序遇到'a'和'A'都应该递增`aCnt`的值,以此类推。
+
+解:
+
+```cpp
+#include
+
+using namespace std;
+
+
+int main()
+{
+ unsigned aCnt = 0, eCnt = 0, iCnt = 0, oCnt = 0, uCnt = 0;
+ char ch;
+ cout << "请输入一段文本" << endl;
+ while (cin >> ch)
+ {
+ switch (ch) {
+ case 'a':
+ case 'A':
+ ++aCnt;
+ break;
+ case 'e':
+ case 'E':
+ ++eCnt;
+ break;
+ case 'I':
+ case 'i':
+ ++iCnt;
+ break;
+ case 'o':
+ case 'O':
+ ++oCnt;
+ break;
+ case 'u':
+ case 'U':
+ ++uCnt;
+ break;
+ }
+
+ }
+ cout << "Number of vowel a: \t" << aCnt << '\n'
+ << "Number of vowel e: \t" << eCnt << '\n'
+ << "Number of vowel i: \t" << iCnt << '\n'
+ << "Number of vowel o: \t" << oCnt << '\n'
+ << "Number of vowel u: \t" << uCnt << '\n'
+ << "Total number: \t" << aCnt + eCnt + iCnt + oCnt +uCnt<< endl;
+
+ return 0;
+}
+```
+
+## 练习5.11
+修改统计元音字母的程序,使其也能统计空格、制表符、和换行符的数量。
+
+解:
+
+```cpp
+#include
+
+using namespace std;
+
+
+int main()
+{
+ unsigned int aCnt = 0, eCnt = 0, iCnt = 0, oCnt = 0, uCnt = 0;
+ unsigned int spaceCnt = 0, tabCnt = 0,newlineCnt = 0;
+ char ch;
+ cout << "请输入一段文本" << endl;
+ while (cin >> ch)
+ {
+ switch (ch) {
+ case 'a':
+ case 'A':
+ ++aCnt;
+ break;
+ case 'e':
+ case 'E':
+ ++eCnt;
+ break;
+ case 'I':
+ case 'i':
+ ++iCnt;
+ break;
+ case 'o':
+ case 'O':
+ ++oCnt;
+ break;
+ case 'u':
+ case 'U':
+ ++uCnt;
+ break;
+ case ' ':
+ ++spaceCnt;
+ break;
+ case '\t':
+ ++tabCnt;
+ break;
+ case '\n':
+ ++newlineCnt;
+ break;
+ }
+
+ }
+ cout << "Number of vowel a: \t" << aCnt << '\n'
+ << "Number of vowel e: \t" << eCnt << '\n'
+ << "Number of vowel i: \t" << iCnt << '\n'
+ << "Number of vowel o: \t" << oCnt << '\n'
+ << "Number of vowel u: \t" << uCnt << '\n'
+ << "Number of space: \t" << spaceCnt << '\n'
+ << "Number of tab: \t" << tabCnt << '\n'
+ << "Number of newline: \t" << newlineCnt << '\n'
+ << "Total number: \t" << aCnt + eCnt + iCnt + oCnt +uCnt + spaceCnt + tabCnt + newlineCnt<< endl;
+
+ return 0;
+}
+```
+
+
+## 练习5.12
+修改统计元音字母的程序,使其能统计含以下两个字符的字符序列的数量:`ff`、`fl`和`fi`。
+
+解:
+
+```cpp
+#include
+
+using namespace std;
+
+
+int main()
+{
+ unsigned int flCnt = 0, ffCnt = 0, fiCnt = 0;
+ char ch, prech= '\0';
+ cout << "请输入一段文本" << endl;
+ while (cin >> ch)
+ {
+ bool flag = true;
+ if (prech == 'f') {
+ switch (ch) {
+ case 'f':
+ ++ffCnt;
+ flag = false;
+ break;
+ case 'l':
+ ++flCnt;
+ break;
+ case 'i':
+ ++flCnt;
+ break;
+ }
+ }
+ if (!flag) {
+ prech = '\0';
+ }else {
+ prech = ch;}
+ }
+ cout << "Number of ff : \t" << ffCnt << '\n'
+ << "Number of fi \t" << fiCnt << '\n'
+ << "Number of fl: \t" << flCnt << '\n'
+ << endl;
+
+ return 0;
+}
+
+
+```
+
+## 练习5.13
+下面显示的每个程序都含有一个常见的编码错误,指出错误在哪里,然后修改它们。
+```cpp
+(a) unsigned aCnt = 0, eCnt = 0, iouCnt = 0;
+ char ch = next_text();
+ switch (ch) {
+ case 'a': aCnt++;
+ case 'e': eCnt++;
+ default: iouCnt++;
+ }
+(b) unsigned index = some_value();
+ switch (index) {
+ case 1:
+ int ix = get_value();
+ ivec[ ix ] = index;
+ break;
+ default:
+ ix = ivec.size()-1;
+ ivec[ ix ] = index;
+ }
+(c) unsigned evenCnt = 0, oddCnt = 0;
+ int digit = get_num() % 10;
+ switch (digit) {
+ case 1, 3, 5, 7, 9:
+ oddcnt++;
+ break;
+ case 2, 4, 6, 8, 10:
+ evencnt++;
+ break;
+ }
+(d) unsigned ival=512, jval=1024, kval=4096;
+ unsigned bufsize;
+ unsigned swt = get_bufCnt();
+ switch(swt) {
+ case ival:
+ bufsize = ival * sizeof(int);
+ break;
+ case jval:
+ bufsize = jval * sizeof(int);
+ break;
+ case kval:
+ bufsize = kval * sizeof(int);
+ break;
+ }
+```
+
+解:
+
+(a) 少了`break`语句。应该为:
+```cpp
+unsigned aCnt = 0, eCnt = 0, iouCnt = 0;
+ char ch = next_text();
+ switch (ch) {
+ case 'a': aCnt++; break;
+ case 'e': eCnt++; break;
+ default: iouCnt++; break;
+ }
+```
+
+(b) 在`default`分支当中,`ix`未定义。应该在外部定义`ix`。
+```cpp
+unsigned index = some_value();
+ int ix;
+ switch (index) {
+ case 1:
+ ix = get_value();
+ ivec[ ix ] = index;
+ break;
+ default:
+ ix = static_cast(ivec.size())-1;
+ ivec[ ix ] = index;
+ }
+```
+
+(c) `case`后面应该用冒号而不是逗号。
+```cpp
+unsigned evenCnt = 0, oddCnt = 0;
+ int digit = get_num() % 10;
+ switch (digit) {
+ case 1: case 3: case 5: case 7: case 9:
+ oddcnt++;
+ break;
+ case 2: case 4: case 6: case 8: case 0:
+ evencnt++;
+ break;
+ }
+```
+
+(d) `case`标签必须是整型常量表达式。
+```cpp
+const unsigned ival=512, jval=1024, kval=4096;
+ unsigned bufsize;
+ unsigned swt = get_bufCnt();
+ switch(swt) {
+ case ival:
+ bufsize = ival * sizeof(int);
+ break;
+ case jval:
+ bufsize = jval * sizeof(int);
+ break;
+ case kval:
+ bufsize = kval * sizeof(int);
+ break;
+ }
+```
+
+## 练习5.14
+编写一段程序,从标准输入中读取若干`string`对象并查找连续重复出现的单词,所谓连续重复出现的意思是:一个单词后面紧跟着这个单词本身。要求记录连续重复出现的最大次数以及对应的单词。如果这样的单词存在,输出重复出现的最大次数;如果不存在,输出一条信息说明任何单词都没有连续出现过。
+例如:如果输入是:
+```
+how now now now brown cow cow
+```
+那么输出应该表明单词now连续出现了3次。
+
+解:
+
+```cpp
+##include
+
+using namespace std;
+
+
+int main()
+{
+ string currStr, preStr = " ", maxStr;
+
+ int currCnt = 1, maxCnt = 0;
+
+
+ while (cin >> currStr) {
+ //
+ if (currStr == preStr) {
+ ++currCnt;
+ if (currCnt > maxCnt) {
+ maxCnt = currCnt;
+ maxStr = currStr;
+ }
+ }else {
+ currCnt = 1;
+ }
+ preStr = currStr;
+ }
+ if (maxCnt >1) {
+ cout << "出现最多的字符串是 : " << maxStr << ", 次数是:" << maxCnt << endl;
+ }else {
+ cout << "每个字符串都出现了一次" << endl;
+ }
+ return 0;
+}
+```
+
+## 练习5.15
+说明下列循环的含义并改正其中的错误。
+
+```cpp
+(a) for (int ix = 0; ix != sz; ++ix) { /* ... */ }
+ if (ix != sz)
+ // . . .
+(b) int ix;
+ for (ix != sz; ++ix) { /* ... */ }
+(c) for (int ix = 0; ix != sz; ++ix, ++sz) { /*...*/ }
+```
+
+解:
+
+应该改为下面这样:
+
+```cpp
+(a) int ix;
+ for (ix = 0; ix != sz; ++ix) { /* ... */ }
+ if (ix != sz)
+ // . . .
+(b) int ix;
+ for (; ix != sz; ++ix) { /* ... */ }
+(c) for (int ix = 0; ix != sz; ++ix) { /*...*/ }
+```
+
+## 练习5.16
+
+`while`循环特别适用于那种条件不变、反复执行操作的情况,例如,当未达到文件末尾时不断读取下一个值。
+`for`循环更像是在按步骤迭代,它的索引值在某个范围内一次变化。根据每种循环的习惯各自编写一段程序,然后分别用另一种循环改写。
+如果只能使用一种循环,你倾向于哪种?为什么?
+
+解:
+
+```cpp
+int i;
+while ( cin >> i )
+ // ...
+
+
+for (int i = 0; cin >> i;)
+ // ...
+
+
+for (int i = 0; i != size; ++i)
+ // ...
+
+
+int i = 0;
+while (i != size)
+{
+ // ...
+ ++i;
+}
+```
+如果只能用一种循环,我会更倾向使用`while`,因为`while`显得简洁,代码可读性强。
+
+## 练习5.17
+
+假设有两个包含整数的`vector`对象,编写一段程序,检验其中一个`vector`对象是否是另一个的前缀。
+为了实现这一目标,对于两个不等长的`vector`对象,只需挑出长度较短的那个,把它的所有元素和另一个`vector`对象比较即可。
+例如,如果两个`vector`对象的元素分别是0、1、1、2 和 0、1、1、2、3、5、8,则程序的返回结果为真。
+
+解:
+
+```cpp
+#include
+#include
+
+using std::cout; using std::vector;
+
+bool is_prefix(vector const& lhs, vector const& rhs)
+{
+ if(lhs.size() > rhs.size())
+ return is_prefix(rhs, lhs);
+ for(unsigned i = 0; i != lhs.size(); ++i)
+ if(lhs[i] != rhs[i]) return false;
+ return true;
+}
+
+int main()
+{
+ vector l{ 0, 1, 1, 2 };
+ vector r{ 0, 1, 1, 2, 3, 5, 8 };
+ cout << (is_prefix(r, l) ? "yes\n" : "no\n");
+
+ return 0;
+}
+```
+
+## 练习5.18
+说明下列循环的含义并改正其中的错误。
+
+```cpp
+(a) do { // 应该添加花括号
+ int v1, v2;
+ cout << "Please enter two numbers to sum:" ;
+ if (cin >> v1 >> v2)
+ cout << "Sum is: " << v1 + v2 << endl;
+ }while (cin);
+(b) int ival;
+ do {
+ // . . .
+ } while (ival = get_response()); // 应该将ival 定义在循环外
+(c) int ival = get_response();
+ do {
+ ival = get_response();
+ } while (ival); // 应该将ival 定义在循环外
+```
+
+## 练习5.19
+编写一段程序,使用`do while`循环重复地执行下述任务:
+首先提示用户输入两个`string`对象,然后挑出较短的那个并输出它。
+
+解:
+
+```cpp
+#include
+#include
+
+using std::cout; using std::cin; using std::endl; using std::string;
+
+int main()
+{
+ string rsp;
+ do {
+ cout << "Input two strings: ";
+ string str1, str2;
+ cin >> str1 >> str2;
+ cout << (str1 <= str2 ? str1 : str2)
+ << " is less than the other. " << "\n\n"
+ << "More? Enter yes or no: ";
+ cin >> rsp;
+ } while (!rsp.empty() && tolower(rsp[0]) == 'y');
+ return 0;
+}
+```
+
+## 练习5.20
+编写一段程序,从标准输入中读取`string`对象的序列直到连续出现两个相同的单词或者所有的单词都读完为止。
+使用`while`循环一次读取一个单词,当一个单词连续出现两次时使用`break`语句终止循环。
+输出连续重复出现的单词,或者输出一个消息说明没有任何单词是连续重复出现的。
+
+解:
+
+```cpp
+#include
+#include
+using std::cout; using std::cin; using std::endl; using std::string;
+
+int main()
+{
+ string read, tmp;
+ while (cin >> read)
+ if (read == tmp) break; else tmp = read;
+
+ if (cin.eof()) cout << "no word was repeated." << endl; //eof(end of file)判断输入是否结束,或者文件结束符,等同于 CTRL+Z
+ else cout << read << " occurs twice in succession." << endl;
+
+ return 0;
+}
+```
+
+## 练习5.21
+修改5.5.1节练习题的程序,使其找到的重复单词必须以大写字母开头。
+
+解:
+
+```cpp
+#include
+using std::cin; using std::cout; using std::endl;
+#include
+using std::string;
+
+int main()
+{
+ string curr, prev;
+ bool no_twice = true;
+ while (cin >> curr)
+ {
+ if (isupper(curr[0]) && prev == curr)
+ {
+ cout << curr << ": occurs twice in succession." << endl;
+ no_twice = false;
+ break;
+ }
+ prev = curr;
+ }
+
+ if (no_twice)
+ cout << "no word was repeated." << endl;
+
+ return 0;
+}
+```
+
+## 练习5.22
+本节的最后一个例子跳回到`begin`,其实使用循环能更好的完成该任务,重写这段代码,注意不再使用`goto`语句。
+
+```cpp
+// 向后跳过一个带初始化的变量定义是合法的
+begin:
+ int sz = get_size();
+ if (sz <= 0) {
+ goto begin;
+ }
+```
+
+解:
+
+用 for 循环修改的话就是这样
+```cpp
+for (int sz = get_size(); sz <=0; sz = get_size())
+ ;
+```
+
+## 练习5.23
+编写一段程序,从标准输入读取两个整数,输出第一个数除以第二个数的结果。
+
+解:
+
+```cpp
+#include
+using std::cin;
+using std::cout;
+using std::endl;
+
+int main()
+{
+ int i, j;
+ cin >> i >> j;
+ cout << i / j << endl;
+
+ return 0;
+}
+```
+
+## 练习5.24
+修改你的程序,使得当第二个数是0时抛出异常。先不要设定`catch`子句,运行程序并真的为除数输入0,看看会发生什么?
+
+解:
+
+```cpp
+#include
+#include
+
+int main(void)
+{
+ int i, j;
+ std::cin >> i >> j;
+ if (j == 0)
+ throw std::runtime_error("divisor is 0");
+ std::cout << i / j << std::endl;
+
+ return 0;
+}
+```
+
+## 练习5.25
+修改上一题的程序,使用`try`语句块去捕获异常。`catch`子句应该为用户输出一条提示信息,询问其是否输入新数并重新执行`try`语句块的内容。
+
+解:
+
+```cpp
+#include
+#include
+using std::cin; using std::cout; using std::endl; using std::runtime_error;
+
+int main(void)
+{
+ for (int i, j; cout << "Input two integers:\n", cin >> i >> j; )
+ {
+ try
+ {
+ if (j == 0)
+ throw runtime_error("divisor is 0");
+ cout << i / j << endl;
+ }
+ catch (runtime_error err)
+ {
+ cout << err.what() << "\nTry again? Enter y or n" << endl;
+ char c;
+ cin >> c;
+ if (!cin || c == 'n')
+ break;
+ }
+ }
+
+ return 0;
+}
+```
\ No newline at end of file
diff --git a/docs/Cpp-Primer-exercise/6.函数.md b/docs/Cpp-Primer-exercise/6.函数.md
new file mode 100644
index 0000000..1d6a5df
--- /dev/null
+++ b/docs/Cpp-Primer-exercise/6.函数.md
@@ -0,0 +1,1063 @@
+---
+sort: 6
+---
+
+# 函数
+
+## 练习6.1
+实参和形参的区别的什么?
+
+解:
+
+形参出现在函数定义的地方,形参列表可以包含0个、1个或多个形参,多个形参之间以逗号分隔。形参规定了一个函数所接受数据的类型和数量。
+实参出现在函数调用的地方,实参的数量与形参一样多。实参的主要作用是初始化形参,并且这种初始化过程是一一对应的,即第一个实参初始化第一个形参、第二个实参初始化第二个形参,以此类推。实参参的类型必须与对应的形参类型匹配。
+
+## 练习6.2
+请指出下列函数哪个有错误,为什么?应该如何修改这些错误呢?
+
+```cpp
+(a) int f() {
+ string s;
+ // ...
+ return s;
+ }
+(b) f2(int i) { /* ... */ }
+(c) int calc(int v1, int v1) { /* ... */ }
+(d) double square (double x) return x * x;
+```
+
+解:
+
+应该改为下面这样:
+
+```cpp
+(a) 定义的类型和返回类型不一致。
+ string f() {
+ string s;
+ // ...
+ return s;
+ }
+(b) 函数缺少返回值类型。void f2(int i) { /* ... */ }
+(c) int calc(int v1, int v2) { /* ... */ return ; }
+(d) double square (double x) { return x * x; }
+```
+
+## 练习6.3
+编写你自己的`fact`函数,上机检查是否正确。注:阶乘。
+
+解:
+
+```cpp
+#include
+
+int fact(int i)
+{
+ if(i<0)
+ {
+ std::runtime_error err("Input cannot be a negative number");
+ std::cout << err.what() << std::endl;
+ }
+ return i > 1 ? i * fact( i - 1 ) : 1;
+}
+
+int main()
+{
+ std::cout << std::boolalpha << (120 == fact(5)) << std::endl;
+ return 0;
+}
+```
+
+启用`std::boolalpha`,可以输出 `"true"`或者 `"false"`。
+
+## 练习6.4
+编写一个与用户交互的函数,要求用户输入一个数字,计算生成该数字的阶乘。在main函数中调用该函数。
+
+```cpp
+#include
+using namespace std;
+
+int fact(int val);
+
+int main() {
+ cout << fact(5) << endl;
+ return 0;
+}
+
+int fact(int val) {
+ if (val == 1) {
+ return 1;
+ }
+ return val > 1 ? val * fact(val - 1): 1;
+}
+```
+
+## 练习6.5
+编写一个函数输出其实参的绝对值。
+
+```cpp
+#include
+
+int abs(int i)
+{
+ return i > 0 ? i : -i;
+}
+
+int main()
+{
+ std::cout << abs(-5) << std::endl;
+ return 0;
+}
+```
+
+## 练习6.6
+说明形参、局部变量以及局部静态变量的区别。编写一个函数,同时达到这三种形式。
+
+解:
+
+* 形参是一种自动化对象,函数开始时为形参申请内存空间,我们用调用函数时提供的实参初始化形参对应的自动对象;
+* 普通变量对应的自动对象也容易理解骂我们在定义该变量的语句处创建自动对象,如果定义语句提供了初始化免责用该值初始化;否则,执行默认初始化。当改变量所在的块结束后,变量失效;
+* 局部静态变量比较特殊,它的生命周期贯穿函数调用及之后的时间。局部静态变量对应的对象成为局部静态对象,他的生命周期从定义语句处开始,直到程序结束才终于。
+
+```cpp
+// 例子
+int count_add(int n) // n是形参
+{
+ static int ctr = 0; // ctr 是局部静态变量
+ ctr += n;
+ return ctr;
+}
+
+int main()
+{
+ for (int i = 0; i != 10; ++i) // i 是局部变量
+ cout << count_add(i) << endl;
+
+ return 0;
+}
+```
+
+## 练习6.7
+编写一个函数,当它第一次被调用时返回0,以后每次被调用返回值加1。
+
+解:
+
+```cpp
+int generate()
+{
+ static int ctr = 0;
+ return ctr++;
+}
+```
+
+## 练习6.8
+编写一个名为Chapter6.h 的头文件,令其包含6.1节练习中的函数声明。
+
+解:
+
+```cpp
+
+int fact(int val);
+int func();
+
+template //参考:https://blog.csdn.net/fightingforcv/article/details/51472586
+
+T abs(T i)
+{
+ return i >= 0 ? i : -i;
+}
+```
+
+## 练习6.9 : fact.cc | factMain.cc
+编写你自己的fact.cc 和factMain.cc ,这两个文件都应该包含上一小节的练习中编写的 Chapter6.h 头文件。通过这些文件,理解你的编译器是如何支持分离式编译的。
+
+解:
+
+fact.cc:
+
+```cpp
+#include "Chapter6.h"
+#include
+
+int fact(int val)
+{
+ if (val == 0 || val == 1) return 1;
+ else return val * fact(val-1);
+}
+
+int func()
+{
+ int n, ret = 1;
+ std::cout << "input a number: ";
+ std::cin >> n;
+ while (n > 1) ret *= n--;
+ return ret;
+}
+
+```
+
+factMain.cc:
+
+```cpp
+#include "Chapter6.h"
+#include
+
+int main()
+{
+ std::cout << "5! is " << fact(5) << std::endl;
+ std::cout << func() << std::endl;
+ std::cout << abs(-9.78) << std::endl;
+}
+```
+
+编译: `g++ factMain.cpp fact.cpp -o main`
+
+## 练习6.10
+编写一个函数,使用指针形参交换两个整数的值。
+在代码中调用该函数并输出交换后的结果,以此验证函数的正确性。
+
+解:
+
+```cpp
+#include
+#include
+
+void swap(int* lhs, int* rhs)
+{
+ int tmp;
+ tmp = *lhs;
+ *lhs = *rhs;
+ *rhs = tmp;
+}
+
+int main()
+{
+ for (int lft, rht; std::cout << "Please Enter:\n", std::cin >> lft >> rht;)
+ {
+ swap(&lft, &rht);
+ std::cout << lft << " " << rht << std::endl;
+ }
+
+ return 0;
+}
+```
+
+## 练习6.11
+编写并验证你自己的reset函数,使其作用于引用类型的参数。注:reset即置0。
+
+解:
+
+```cpp
+#include
+
+void reset(int &i)
+{
+ i = 0;
+}
+
+int main()
+{
+ int i = 42;
+ reset(i);
+ std::cout << i << std::endl;
+ return 0;
+}
+```
+
+## 练习6.12
+改写6.2.1节练习中的程序,使其引用而非指针交换两个整数的值。你觉得哪种方法更易于使用呢?为什么?
+
+```cpp
+#include
+using namespace std;
+
+void swap(int &p, int &q) {
+ int temp = p;
+ p = q;
+ q = temp;
+}
+
+int main() {
+ int a = 5, b = 3;
+ swap(a, b);
+ cout << "交换后:a= " << a << ", b =" << b << endl;
+ return 0;
+}
+
+```
+与使用指针相比,使用引用交换变量的内容从形式上看更简单一些,并且无须额外声明指针变量,也避免了拷贝指针的值。
+
+## 练习6.13
+假设`T`是某种类型的名字,说明以下两个函数声明的区别:
+一个是`void f(T)`, 另一个是`void f(&T)`。
+
+解:
+
+`void f(T)`的参数通过值传递,在函数中`T`是实参的副本,改变`T`不会影响到原来的实参。
+`void f(&T)`的参数通过引用传递,在函数中的`T`是实参的引用,`T`的改变也就是实参的改变。
+
+## 练习6.14
+举一个形参应该是引用类型的例子,再举一个形参不能是引用类型的例子。
+
+解:
+1. 当函数的目的是交换两个参数的内容时,应该使用引用类型的形参;
+2. 当参数是string对象时,为了避免拷贝很长的字符串,应该使用引用类型。
+例如交换两个整数的函数,形参应该是引用
+
+```cpp
+void swap(int& lhs, int& rhs)
+{
+ int temp = lhs;
+ lhs = rhs;
+ rhs = temp;
+}
+```
+
+当实参的值是右值时,形参不能为引用类型
+
+```cpp
+int add(int a, int b)
+{
+ return a + b;
+}
+
+int main()
+{
+ int i = add(1,2);
+ return 0;
+}
+```
+
+## 练习6.15
+说明`find_char`函数中的三个形参为什么是现在的类型,特别说明为什么`s`是常量引用而`occurs`是普通引用?
+为什么`s`和`occurs`是引用类型而`c`不是?
+如果令`s`是普通引用会发生什么情况?
+如果令`occurs`是常量引用会发生什么情况?
+
+解:
+
+- 因为字符串可能很长,因此使用引用避免拷贝;
+- 而在函数中我们不希望改变`s`的内容,所以令`s`为常量。
+- `occurs`是要传到函数外部的变量,所以使用引用,`occurs`的值会改变,所以是普通引用。
+- 因为我们只需要`c`的值,这个实参可能是右值(右值实参无法用于引用形参),所以`c`不能用引用类型。
+- 如果`s`是普通引用,也可能会意外改变原来字符串的内容。
+- `occurs`如果是常量引用,那么意味着不能改变它的值,那也就失去意义了。
+
+## 练习6.16
+下面的这个函数虽然合法,但是不算特别有用。指出它的局限性并设法改善。
+```cpp
+bool is_empty(string& s) { return s.empty(); }
+```
+
+解:
+
+局限性在于常量字符串和字符串字面值无法作为该函数的实参,如果下面这样调用是非法的:
+
+```cpp
+const string str;
+bool flag = is_empty(str); //非法
+bool flag = is_empty("hello"); //非法
+```
+
+所以要将这个函数的形参定义为常量引用:
+
+```cpp
+bool is_empty(const string& s) { return s.empty(); }
+```
+
+## 练习6.17
+编写一个函数,判断`string`对象中是否含有大写字母。
+编写另一个函数,把`string`对象全部改写成小写形式。
+在这两个函数中你使用的形参类型相同吗?为什么?
+
+解:
+
+两个函数的形参不一样。第一个函数使用常量引用,第二个函数使用普通引用。
+
+## 练习6.18
+为下面的函数编写函数声明,从给定的名字中推测函数具备的功能。
+
+- (a) 名为`compare`的函数,返回布尔值,两个参数都是`matrix`类的引用。
+- (b) 名为`change_val`的函数,返回`vector`的迭代器,有两个参数:一个是`int`,另一个是`vector`的迭代器。
+
+解:
+
+```cpp
+(a) bool compare(matrix &m1, matrix &m2);
+(b) vector::iterator change_val(int, vector::iterator);
+```
+
+## 练习6.19
+假定有如下声明,判断哪个调用合法、哪个调用不合法。对于不合法的函数调用,说明原因。
+
+```cpp
+double calc(double);
+int count(const string &, char);
+int sum(vector::iterator, vector::iterator, int);
+vector vec(10);
+(a) calc(23.4, 55.1);
+(b) count("abcda",'a');
+(c) calc(66);
+(d) sum(vec.begin(), vec.end(), 3.8);
+```
+
+解:
+
+- (a) 不合法。`calc`只有一个参数。
+- (b) 合法。
+- (c) 合法。
+- (d) 合法。
+
+## 练习6.20
+引用形参什么时候应该是常量引用?如果形参应该是常量引用,而我们将其设为了普通引用,会发生什么情况?
+
+解:
+
+应该尽量将引用形参设为常量引用,除非有明确的目的是为了改变这个引用变量。
+如果形参应该是常量引用,而我们将其设为了普通引用,那么常量实参将无法作用于普通引用形参。
+
+## 练习6.21
+编写一个函数,令其接受两个参数:一个是`int`型的数,另一个是`int`指针。
+函数比较`int`的值和指针所指的值,返回较大的那个。
+在该函数中指针的类型应该是什么?
+
+解:
+
+```cpp
+##include
+#include
+#include
+#include
+
+using namespace std;
+
+int compare(const int *p, const int val) { // 判断是否含有大写字母
+ return (val > *p) ? val : *p;
+}
+
+
+
+
+int main() {
+
+ srand((unsigned) time(NULL));
+ int a[10];
+
+ for(auto &i : a)
+ i = rand() % 100;
+
+ cout << "请输入一个数" <> j;
+ cout<< "较大的数是" << compare(a, j) << endl;
+ return 0;
+}
+
+```
+
+应该是`const int *`类型。
+
+## 练习6.22
+编写一个函数,令其交换两个`int`指针。
+
+解:
+
+```cpp
+#include
+#include
+#include
+#include
+
+using namespace std;
+
+int swap(int *p, int *q) { // 判断是否含有大写字母
+ int temp = *p;
+ *p = *q;
+ *q = temp;
+}
+
+int main() {
+ int a = 5, b =10;
+ int *p = &a, *q = &b;
+ swap(p, q);
+ cout << "a=" <
+#include
+
+int sum(std::initializer_list const& il)
+{
+ int sum = 0;
+ for (auto i : il) sum += i;
+ return sum;
+}
+
+int main(void)
+{
+ auto il = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+ std::cout << sum(il) << std::endl;
+
+ return 0;
+}
+```
+
+## 练习6.28
+在`error_msg`函数的第二个版本中包含`ErrCode`类型的参数,其中循环内的`elem`是什么类型?
+
+解:
+
+`elem`是`const string &`类型。
+
+## 练习6.29
+在范围`for`循环中使用`initializer_list`对象时,应该将循环控制变量声明成引用类型吗?为什么?
+
+解:
+
+应该使用常量引用类型。`initializer_list`对象中的元素都是常量,我们无法修改`initializer_list`对象中的元素的值。
+
+## 练习6.30
+编译第200页的`str_subrange`函数,看看你的编译器是如何处理函数中的错误的。
+
+解:
+
+编译器信息:
+```
+g++ (Ubuntu 5.4.0-6ubuntu1~16.04.10) 5.4.0 20160609
+```
+
+编译错误信息:
+
+```
+ch6.cpp:38:9: error: return-statement with no value, in function returning ‘bool’ [-fpermissive]
+```
+
+## 练习6.31
+什么情况下返回的引用无效?什么情况下返回常量的引用无效?
+
+解:
+
+当返回的引用的对象是局部变量时,返回的引用无效;当我们希望返回的对象被修改时,返回常量的引用无效。
+
+## 练习6.32
+下面的函数合法吗?如果合法,说明其功能;如果不合法,修改其中的错误并解释原因。
+
+```cpp
+int &get(int *array, int index) { return array[index]; }
+int main()
+{
+ int ia[10];
+ for (int i = 0; i != 10; ++i)
+ get(ia, i) = i;
+}
+```
+
+解:
+
+合法。`get`函数根据索引取得数组中的元素的引用。
+
+## 练习6.33
+编写一个递归函数,输出`vector`对象的内容。
+
+解:
+
+```cpp
+#include
+#include
+using std::vector; using std::cout;
+using Iter = vector::const_iterator;
+
+void print(Iter first, Iter last)
+{
+ if (first != last)
+ {
+ cout << *first << " ";
+ print(++first, last);
+ }
+}
+
+int main()
+{
+ vector vec{ 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+ print(vec.cbegin(), vec.cend());
+
+ return 0;
+}
+```
+
+## 练习6.34
+如果`factorial`函数的停止条件如下所示,将发生什么?
+
+```cpp
+if (val != 0)
+```
+
+解:
+如果`val`为正数,从结果上来说没有区别(多乘了个1);
+如果`val`为负数,那么递归永远不会结束。
+
+## 练习6.35
+在调用`factorial`函数时,为什么我们传入的值是`val-1`而非`val--`?
+
+解:
+
+如果传入的值是`val--`,变量的递减操作与读取变量值的操作共存于同一个表达式中,这时有可能产生未定义的值。
+
+## 练习6.36
+编写一个函数声明,使其返回数组的引用并且该数组包含10个`string`对象。
+不用使用尾置返回类型、`decltype`或者类型别名。
+
+解:
+
+```cpp
+string (&fun())[10];
+```
+
+## 练习6.37
+为上一题的函数再写三个声明,一个使用类型别名,另一个使用尾置返回类型,最后一个使用`decltype`关键字。
+你觉得哪种形式最好?为什么?
+
+解:
+
+```cpp
+typedef string str_arr[10];
+str_arr& fun();
+
+auto fun()->string(&)[10];
+
+string s[10];
+decltype(s)& fun();
+```
+
+我觉得尾置返回类型最好,就一行代码。
+
+## 练习6.38
+修改`arrPtr`函数,使其返回数组的引用。
+
+解:
+
+```cpp
+decltype(odd)& arrPtr(int i)
+{
+ return (i % 2) ? odd : even;
+}
+```
+
+## 练习6.39
+说明在下面的每组声明中第二条语句是何含义。
+如果有非法的声明,请指出来。
+
+```cpp
+(a) int calc(int, int);
+ int calc(const int, const int);
+(b) int get();
+ double get();
+(c) int *reset(int *);
+ double *reset(double *);
+```
+
+解:
+
+- (a) 非法。因为顶层const不影响传入函数的对象,所以第二个声明无法与第一个声明区分开来。
+- (b) 非法。对于重载的函数来说,它们应该只有形参的数量和形参的类型不同。返回值与重载无关。
+- (c) 合法。
+
+## 练习6.40
+下面的哪个声明是错误的?为什么?
+
+```cpp
+(a) int ff(int a, int b = 0, int c = 0);
+(b) char *init(int ht = 24, int wd, char bckgrnd);
+```
+
+解:
+
+(a) 正确。
+(b) 错误。因为一旦某个形参被赋予了默认值,那么它之后的形参都必须要有默认值。
+
+## 练习6.41
+下面的哪个调用是非法的?为什么?哪个调用虽然合法但显然与程序员的初衷不符?为什么?
+
+```cpp
+char *init(int ht, int wd = 80, char bckgrnd = ' ');
+(a) init();
+(b) init(24,10);
+(c) init(14,'*');
+```
+
+解:
+
+- (a) 非法。第一个参数不是默认参数,最少需要一个实参。
+- (b) 合法。
+- (c) 合法,但与初衷不符。字符`*`被解释成`int`传入到了第二个参数。而初衷是要传给第三个参数。
+
+## 练习6.42
+给`make_plural`函数的第二个形参赋予默认实参's', 利用新版本的函数输出单词success和failure的单数和复数形式。
+
+解:
+
+```cpp
+#include
+#include
+
+using std::string;
+using std::cout;
+using std::endl;
+
+string make_plural(size_t ctr, const string& word, const string& ending = "s")
+{
+ return (ctr > 1) ? word + ending : word;
+}
+
+int main()
+{
+ cout << "single: " << make_plural(1, "success", "es") << " "
+ << make_plural(1, "failure") << endl;
+ cout << "plural : " << make_plural(2, "success", "es") << " "
+ << make_plural(2, "failure") << endl;
+
+ return 0;
+}
+```
+
+## 练习6.43
+你会把下面的哪个声明和定义放在头文件中?哪个放在源文件中?为什么?
+
+```cpp
+(a) inline bool eq(const BigInt&, const BigInt&) {...}
+(b) void putValues(int *arr, int size);
+```
+
+解:
+
+全部都放进头文件。(a) 是内联函数,(b) 是声明。
+
+## 练习6.44
+将6.2.2节的`isShorter`函数改写成内联函数。
+
+解:
+
+```cpp
+inline bool is_shorter(const string &lft, const string &rht)
+{
+ return lft.size() < rht.size();
+}
+```
+
+## 练习6.45
+回顾在前面的练习中你编写的那些函数,它们应该是内联函数吗?
+如果是,将它们改写成内联函数;如果不是,说明原因。
+
+解:
+
+一般来说,内联机制用于优化规模小、流程直接、频繁调用的函数。
+
+## 练习6.46
+能把`isShorter`函数定义成`constexpr`函数吗?
+如果能,将它改写成`constxpre`函数;如果不能,说明原因。
+
+解:
+
+不能。`constexpr`函数的返回值类型及所有形参都得是字面值类型。
+
+## 练习6.47
+改写6.3.2节练习中使用递归输出`vector`内容的程序,使其有条件地输出与执行过程有关的信息。
+例如,每次调用时输出`vector`对象的大小。
+分别在打开和关闭调试器的情况下编译并执行这个程序。
+
+解:
+
+```cpp
+#include
+#include
+using std::vector; using std::cout; using std::endl;
+
+void printVec(vector &vec)
+{
+#ifndef NDEBUG
+ cout << "vector size: " << vec.size() << endl;
+#endif
+ if (!vec.empty())
+ {
+ auto tmp = vec.back();
+ vec.pop_back();
+ printVec(vec);
+ cout << tmp << " ";
+ }
+}
+
+int main()
+{
+ vector vec{ 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+ printVec(vec);
+ cout << endl;
+
+ return 0;
+}
+```
+
+## 练习6.48
+说明下面这个循环的含义,它对assert的使用合理吗?
+
+```cpp
+string s;
+while (cin >> s && s != sought) { } //空函数体
+assert(cin);
+```
+
+解:
+
+不合理。从这个程序的意图来看,应该用
+
+```cpp
+assert(s == sought);
+```
+
+## 练习6.49
+什么是候选函数?什么是可行函数?
+
+解:
+
+候选函数:与被调用函数同名,并且其声明在调用点可见。
+可行函数:形参与实参的数量相等,并且每个实参类型与对应的形参类型相同或者能转换成形参的类型。
+
+## 练习6.50
+已知有第217页对函数`f`的声明,对于下面的每一个调用列出可行函数。
+其中哪个函数是最佳匹配?
+如果调用不合法,是因为没有可匹配的函数还是因为调用具有二义性?
+
+```cpp
+(a) f(2.56, 42)
+(b) f(42)
+(c) f(42, 0)
+(d) f(2.56, 3.14)
+```
+
+解:
+
+- (a) `void f(int, int);`和`void f(double, double = 3.14);`是可行函数。
+该调用具有二义性而不合法。
+- (b) `void f(int);` 是可行函数。调用合法。
+- (c) `void f(int, int);`和`void f(double, double = 3.14);`是可行函数。
+`void f(int, int);`是最佳匹配。
+- (d) `void f(int, int);`和`void f(double, double = 3.14);`是可行函数。
+`void f(double, double = 3.14);`是最佳匹配。
+
+## 练习6.51
+编写函数`f`的4版本,令其各输出一条可以区分的消息。
+验证上一个练习的答案,如果你的回答错了,反复研究本节内容直到你弄清自己错在何处。
+
+解:
+
+```cpp
+#include
+using std::cout; using std::endl;
+
+void f()
+{
+ cout << "f()" << endl;
+}
+
+void f(int)
+{
+ cout << "f(int)" << endl;
+}
+
+void f(int, int)
+{
+ cout << "f(int, int)" << endl;
+}
+
+void f(double, double)
+{
+ cout << "f(double, double)" << endl;
+}
+
+int main()
+{
+ //f(2.56, 42); // error: 'f' is ambiguous.
+ f(42);
+ f(42, 0);
+ f(2.56, 3.14);
+
+ return 0;
+}
+
+```
+
+## 练习6.52
+已知有如下声明:
+```cpp
+void manip(int ,int);
+double dobj;
+```
+请指出下列调用中每个类型转换的等级。
+
+```cpp
+(a) manip('a', 'z');
+(b) manip(55.4, dobj);
+```
+
+解:
+
+- (a) 第3级。类型提升实现的匹配。
+- (b) 第4级。算术类型转换实现的匹配。
+
+## 练习6.53
+说明下列每组声明中的第二条语句会产生什么影响,并指出哪些不合法(如果有的话)。
+
+
+```cpp
+(a) int calc(int&, int&);
+ int calc(const int&, const int&);
+(b) int calc(char*, char*);
+ int calc(const char*, const char*);
+(c) int calc(char*, char*);
+ int calc(char* const, char* const);
+```
+
+解:
+
+(c) 不合法。顶层const不影响传入函数的对象。
+
+## 练习6.54
+编写函数的声明,令其接受两个`int`形参并返回类型也是`int`;然后声明一个`vector`对象,令其元素是指向该函数的指针。
+
+解:
+
+```cpp
+int func(int, int);
+vector v;
+```
+
+## 练习6.55
+编写4个函数,分别对两个`int`值执行加、减、乘、除运算;在上一题创建的`vector`对象中保存指向这些函数的指针。
+
+解:
+
+```cpp
+int add(int a, int b) { return a + b; }
+int subtract(int a, int b) { return a - b; }
+int multiply(int a, int b) { return a * b; }
+int divide(int a, int b) { return b != 0 ? a / b : 0; }
+
+v.push_back(add);
+v.push_back(subtract);
+v.push_back(multiply);
+v.push_back(divide);
+```
+
+## 练习6.56
+调用上述`vector`对象中的每个元素并输出结果。
+
+解:
+
+```cpp
+std::vector vec{ add, subtract, multiply, divide };
+for (auto f : vec)
+ std::cout << f(2, 2) << std::endl;
+```
\ No newline at end of file
diff --git a/docs/Cpp-Primer-exercise/7.类.md b/docs/Cpp-Primer-exercise/7.类.md
new file mode 100644
index 0000000..4fb2296
--- /dev/null
+++ b/docs/Cpp-Primer-exercise/7.类.md
@@ -0,0 +1,1370 @@
+---
+sort: 7
+---
+
+
+# 类
+
+## 练习7.1
+
+使用2.6.1节定义的`Sales_data`类为1.6节的交易处理程序编写一个新版本。
+
+解:
+
+```cpp
+#include
+#include
+using std::cin; using std::cout; using std::endl; using std::string;
+
+struct Sales_data
+{
+ string bookNo;
+ unsigned units_sold = 0;
+ double revenue = 0.0;
+};
+
+int main()
+{
+ Sales_data total;
+ if (cin >> total.bookNo >> total.units_sold >> total.revenue)
+ {
+ Sales_data trans;
+ while (cin >> trans.bookNo >> trans.units_sold >> trans.revenue)
+ {
+ if (total.bookNo == trans.bookNo)
+ {
+ total.units_sold += trans.units_sold;
+ total.revenue += trans.revenue;
+ }
+ else
+ {
+ cout << total.bookNo << " " << total.units_sold << " " << total.revenue << endl;
+ total = trans;
+ }
+ }
+ cout << total.bookNo << " " << total.units_sold << " " << total.revenue << endl;
+ }
+ else
+ {
+ std::cerr << "No data?!" << std::endl;
+ return -1;
+ }
+ return 0;
+}
+```
+
+## 练习7.2
+
+曾在2.6.2节的练习中编写了一个`Sales_data`类,请向这个类添加`combine`函数和`isbn`成员。
+
+解:
+
+```cpp
+#include
+
+struct Sales_data {
+ std::string isbn() const { return bookNo; };
+ Sales_data& combine(const Sales_data&);
+
+ std::string bookNo;
+ unsigned units_sold = 0;
+ double revenue = 0.0;
+};
+
+Sales_data& Sales_data::combine(const Sales_data& rhs)
+{
+ units_sold += rhs.units_sold;
+ revenue += rhs.revenue;
+ return *this;
+}
+```
+
+## 练习7.3
+
+修改7.1.1节的交易处理程序,令其使用这些成员。
+
+解:
+
+```cpp
+#include
+using std::cin; using std::cout; using std::endl;
+
+int main()
+{
+ Sales_data total;
+ if (cin >> total.bookNo >> total.units_sold >> total.revenue)
+ {
+ Sales_data trans;
+ while (cin >> trans.bookNo >> trans.units_sold >> trans.revenue) {
+ if (total.isbn() == trans.isbn())
+ total.combine(trans);
+ else {
+ cout << total.bookNo << " " << total.units_sold << " " << total.revenue << endl;
+ total = trans;
+ }
+ }
+ cout << total.bookNo << " " << total.units_sold << " " << total.revenue << endl;
+ }
+ else
+ {
+ std::cerr << "No data?!" << std::endl;
+ return -1;
+ }
+ return 0;
+}
+```
+
+## 练习7.4
+
+编写一个名为`Person`的类,使其表示人员的姓名和地址。使用`string`对象存放这些元素,接下来的练习将不断充实这个类的其他特征。
+
+解:
+
+```cpp
+#include
+
+class Person {
+ std::string name;
+ std::string address;
+};
+```
+
+## 练习7.5
+
+在你的`Person`类中提供一些操作使其能够返回姓名和地址。
+这些函数是否应该是`const`的呢?解释原因。
+
+解:
+
+```cpp
+#include
+
+class Person
+{
+ std::string name;
+ std::string address;
+public:
+ auto get_name() const -> std::string const& { return name; }
+ auto get_addr() const -> std::string const& { return address; }
+};
+```
+应该是`const`的。因为常量的`Person`对象也需要使用这些函数操作。
+
+## 练习7.6
+
+对于函数`add`、`read`和`print`,定义你自己的版本。
+
+解:
+
+```cpp
+#include
+#include
+
+struct Sales_data {
+ std::string const& isbn() const { return bookNo; };
+ Sales_data& combine(const Sales_data&);
+
+ std::string bookNo;
+ unsigned units_sold = 0;
+ double revenue = 0.0;
+};
+
+// member functions.
+Sales_data& Sales_data::combine(const Sales_data& rhs)
+{
+ units_sold += rhs.units_sold;
+ revenue += rhs.revenue;
+ return *this;
+}
+
+// nonmember functions
+std::istream &read(std::istream &is, Sales_data &item)
+{
+ double price = 0;
+ is >> item.bookNo >> item.units_sold >> price;
+ item.revenue = price * item.units_sold;
+ return is;
+}
+
+std::ostream &print(std::ostream &os, const Sales_data &item)
+{
+ os << item.isbn() << " " << item.units_sold << " " << item.revenue;
+ return os;
+}
+
+Sales_data add(const Sales_data &lhs, const Sales_data &rhs)
+{
+ Sales_data sum = lhs;
+ sum.combine(rhs);
+ return sum;
+}
+```
+
+## 练习7.7
+
+使用这些新函数重写7.1.2节练习中的程序。
+
+```cpp
+int main()
+{
+ Sales_data total;
+ if (read(std::cin, total))
+ {
+ Sales_data trans;
+ while (read(std::cin, trans)) {
+ if (total.isbn() == trans.isbn())
+ total.combine(trans);
+ else {
+ print(std::cout, total) << std::endl;
+ total = trans;
+ }
+ }
+ print(std::cout, total) << std::endl;
+ }
+ else
+ {
+ std::cerr << "No data?!" << std::endl;
+ return -1;
+ }
+
+ return 0;
+}
+```
+
+## 练习7.8
+
+为什么`read`函数将其`Sales_data`参数定义成普通的引用,而`print`函数将其参数定义成常量引用?
+
+解:
+
+因为`read`函数会改变对象的内容,而`print`函数不会。
+
+## 练习7.9
+
+对于7.1.2节练习中代码,添加读取和打印`Person`对象的操作。
+
+解:
+
+```cpp
+#include
+#include
+
+struct Person
+{
+ std::string const& getName() const { return name; }
+ std::string const& getAddress() const { return address; }
+
+ std::string name;
+ std::string address;
+};
+
+std::istream &read(std::istream &is, Person &person)
+{
+ return is >> person.name >> person.address;
+}
+
+std::ostream &print(std::ostream &os, const Person &person)
+{
+ return os << person.name << " " << person.address;
+}
+```
+
+## 练习7.10
+
+在下面这条`if`语句中,条件部分的作用是什么?
+
+```cpp
+if (read(read(cin, data1), data2)) //等价read(std::cin, data1);read(std::cin, data2);
+```
+
+解:
+
+`read`函数的返回值是`istream`对象,
+`if`语句中条件部分的作用是从输入流中读取数据给两个`data`对象。
+
+## 练习7.11 :
+
+在你的`Sales_data`类中添加构造函数,
+然后编写一段程序令其用到每个构造函数。
+
+解:
+
+头文件:
+
+```cpp
+#include
+#include
+
+struct Sales_data {
+ Sales_data() = default;
+ Sales_data(const std::string &s):bookNo(s) { }
+ Sales_data(const std::string &s, unsigned n, double p):bookNo(s), units_sold(n), revenue(n*p){ }
+ Sales_data(std::istream &is);
+
+ std::string isbn() const { return bookNo; };
+ Sales_data& combine(const Sales_data&);
+
+ std::string bookNo;
+ unsigned units_sold = 0;
+ double revenue = 0.0;
+};
+
+// nonmember functions
+std::istream &read(std::istream &is, Sales_data &item)
+{
+ double price = 0;
+ is >> item.bookNo >> item.units_sold >> price;
+ item.revenue = price * item.units_sold;
+ return is;
+}
+
+std::ostream &print(std::ostream &os, const Sales_data &item)
+{
+ os << item.isbn() << " " << item.units_sold << " " << item.revenue;
+ return os;
+}
+
+Sales_data add(const Sales_data &lhs, const Sales_data &rhs)
+{
+ Sales_data sum = lhs;
+ sum.combine(rhs);
+ return sum;
+}
+
+// member functions.
+Sales_data::Sales_data(std::istream &is)
+{
+ read(is, *this);
+}
+
+Sales_data& Sales_data::combine(const Sales_data& rhs)
+{
+ units_sold += rhs.units_sold;
+ revenue += rhs.revenue;
+ return *this;
+}
+```
+
+主函数:
+
+```cpp
+int main()
+{
+ Sales_data item1;
+ print(std::cout, item1) << std::endl;
+
+ Sales_data item2("0-201-78345-X");
+ print(std::cout, item2) << std::endl;
+
+ Sales_data item3("0-201-78345-X", 3, 20.00);
+ print(std::cout, item3) << std::endl;
+
+ Sales_data item4(std::cin);
+ print(std::cout, item4) << std::endl;
+
+ return 0;
+}
+```
+
+## 练习7.12
+
+把只接受一个`istream`作为参数的构造函数移到类的内部。
+
+解:
+
+```cpp
+#include
+#include