# c_Primer_Plus_exercise
**Repository Path**: lerenhua/c_Primer_Plus_exercise
## Basic Information
- **Project Name**: c_Primer_Plus_exercise
- **Description**: C++ Primer Plus 第五版编程练习题集合
- **Primary Language**: C++
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2019-09-27
- **Last Updated**: 2020-12-19
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# c_Primer_Plus_exercise
C++ Primer Plus 第五版编程练习题集合
此repo用于记录书本课后编程习题代码,记录自己的学习进度
## 目录
* [chapter 2](#chapyer-2) :开始学习C++
* [chapter 3](#chapter-3) :处理数据
* [chapter 4](#chapter-4) :复合类型
* [数组](#数组)
* [字符串](#字符串)
* [string类简介](#string类简介)
* [结构简介](#结构简介)
* [共用体](#共用体)
* [枚举](#枚举)
* [指针和自由存储空间](#指针和自由存储空间)
* [指针,数组和指针算术](#指针数组和指针算术)
* [chapter 5](#chapter-5) :循环和关系表达式
* [for循环](#for循环)
* [关系表达式](#关系表达式)
* [while 循环](#while-循环)
* [do while 循环](#do-while-循环)
* [二维数组](#二维数组)
* [类型别名](#类型别名)
* [chapter 6](#chapter-6) :分支语句和逻辑操作符
* [if语句及if-else语句](#if语句及if-else语句)
* [逻辑表达式](#逻辑表达式)
* [switch语句](#switch语句)
* [break和continue语句](#break和continue语句)
* [读取数字的循环](#读取数字的循环)
* [chapter 7](#chapter-7) :函数—C++的编程模块
* [C++函数基本知识](#C++函数基本知识)
* [函数参数与按值传递](#函数参数与按值传递)
## 章节内容概述
### chapter 2
* C++程序的一般形式:
* 注释, 由//前缀指示。
* 预处理器编译指令 #include。
* 函数头 int main() 。
* 编译指令,如using namespace。
* 函数体,使用{}括起。
* 结束 main 函数的返回语句。
* C++基本输入输出方法:cin ,cout
* 名称空间与类的简介
* C++语句
* 声明语句及变量。
* 赋值语句。
* 输入输出语句。
* 函数
1. 使用有返回值的函数。
2. 函数变体 -- 某些语言中,有返回值函数为函数(function),无返回值函数为过程(procedure)或子程序(subroutine),在C++中两种变体统称函数。
3. 用户定义的函数:3.1 函数原型; 3.2 函数定义
### chapter 3
* 变量命名规则
1. 只能使用字母,数字和下划线(_)。
2. 名称第一个不能是数字。
3. 区分大小写。
4. 不能使用C++关键字作名称。
5. 以两个下划线或下划线和大写字母开头的名称被保留给实现(编译器及其使用的资源)使用。以一个下划线开头的名称保留给实现,用作全局标识符。(此处合法但是可能出现问题)
6. C++对名称长度无限制。
* C++内置整型
1. unsigned long , long , unsigned int , int , unsigned short , short
2. char , unsigned char , signed char , bool
3. C++中short, int, long的最小长度标准:
3.1 short至少16位。
3.2 int至少和short一样长。
3.3 long至少32位,且至少与int一样长。(具体整型长度参阅头文件climits)
4. char类型:专门用于存储字符及小数
5. signed char 与 unsigned char: 默认情况下,char本身既不是没有符号也不是有符号,而依赖于C++实现,如果char作数值类型使用时,可显式确定有无符号。
6. wcha_t类型:用于解决char类型字长较小无法表示更大字符集的宽字符类型,其底层类型的选择取决于实现,如一个系统可能以unsigned short实现而另一系统为int;由于cin,cout输入输出只适用于char流,因此需要wcin和wcout实现wcha_t流输入输出;另外可以通过加前缀‘L’来指示宽字符常量及宽字符串。
7. bool类型: 非零值为true,零值为false;任何数值或指针值都可以隐式转换成bool值。
* const限定符
* 用于声明符号常量。
* 相对于#define,推荐使用const定义符号常量。
* C++内置浮点类型
1. float, double, long double
2. 位数要求:float至少32位,double至少48位且不少于float,long double至少和double一样多。(实际上,通常float 32位,double 64位,long double 为80,96,128位)
* C++算术操作符
* 加(+),减(-),乘(*),除(/), 取模求余数(%)。
* %只用于整数。
* 类型转换
1. 类型自动转换的情况:
* 将一种算术类型的值赋值给另一种算术类型的变量时,自动转换。
* 表达式中包含不同类型值时,自动转换。
* 将参数传给函数时,自动转换。
2. 强制类型转换
* C++风格形式:typeName(value)
* 强制类型转换不改变变量本身,而是创建新的指定类型的值。
### chapter 4
#### 数组
* 继承于C语言的数组,使用方法一致
* 声明语句要素:
1. 存储在每个元素的值的类型
2. 数组名
3. 数组中的元素个数 (特殊情况:定义与初始化一起时可不指定个数)
* 初始化规则
1. `int cards[4] = {3, 6, 8, 10};`(合法)
2. `int hand[4];`(合法)
3. `long totals[500] = {5, 2};`(部分初始化,其余为0;合法)
4. `short things[] = {1, 5, 3, 8};`(可不指明个数初始化,编译时编译器会计算出个数)
* 注意
1. 使用数组时,要使用有效下标值。因为编译器不会检查下标是否有效或越界。
2. 只有定义数组时才能初始化,之后无法使用数组赋值数组的方式
#### 字符串
##### C-风格字符串(c-style string)
* C-风格字符串性质:以空字符(null character)结尾,空字符被写作`\0`,用以标记字符串结尾,如
`char dog[5] = {'b', 'e', 'a', 'u', 'x'}` 非字符串, `char cat[5] = {'f', 'a', 't', 's', '\0'}` 为字符串。
* 字符串常量(使用双引号)与字符常量(使用单引号)不能互换。
* 拼接字符串常量:
* 任何两个由空白符(空格,制表符,换行符)分隔的字符串常量自动拼接,如`cout << "I'd give my right arm to be" "a great violinist.\n";` 等价 `cout << "I'd give my right arm to be a great violinist.\n";`
* 在数组中使用字符串
* 字符串输入
* `cin`在接收输入时只读取一个单词,并以空白来界定字符串的界。
* 输入的字符串长度可能大于数组长度
* 每次读取一行字符串输入
1. 面向行的输入:`cin.getline(arrayName, size)`
2. 面向行的输入:`cin.get(arrayName, size)`
3. 注意: `getline()`通过换行符确定结尾,并舍弃换行符; 而 `get()`会保留换行符在队列中,因此
```C++
cin.get(name, size);
cin.get(dessert, size);
```
连续调用`get()`时第二个无法读取内容。
#### string类简介
* string类与字符数组相同之处:
* 可以使用c-风格字符串初始化string对象。
* 可以使用cin键盘输入存储到string对象。
* 可以使用cout显示string对象。
* 可以使用数组表示法来访问存储在string对象中的字符。
* 赋值,拼接和附加
```C++
string str1, str2, str3;
str3 = str1 + str2;
str3 += str2;
```
#### 结构简介
* 定义结构描述
```C++
struct inflatable
{
char name[20];
float volume;
double price;
}
```
列表中每一项为结构成员,因此,inflatable结构有3个成员。
* 创建结构变量
```C++
inflatable hat;
struct inflatable goose;
```
关键字struct可省略。hat类型为inflatable,可以使用成员操作符(.)来访问各个成员。
* 结构数组
```C++
//可创建元素为结构的数组
inflatable gifts[100]; // 声明包含100个inflatable结构的数组
inflatable guests[2] = {
{"Bambi", 0.5, 21.99},
{"Godzilla", 2000, 565.99}
}; // 声明2个包含inflatable结构的数组,并初始化
```
* 定义好的结构,可理解为一种新数据类型,与int,float等类型概念相同,因此可以直接默认为数据类型。
#### 共用体
* 共用体(union)是一种数据格式,能存储不同的数据类型,但同时只能存储一种类型。与结构体不同,结构体可同时存储多种类型。
* 声明及使用:
```C++
union one4all
{
int int_val;
long long_val;
double double_val;
}; // one4all可以存储多种类型,但一次只能存储一种类型的值
one4all pail;
pail.int_val = 15; // 存储了一个int值
cout << pail.int_val;
pail.double_val = 1.38; // 存储了一个double值,同时丢失了int值
cout << pail.double_val;
```
* 共用体长度为其最大成员长度
* 当数据项使用两种或更多格式(但不会同时使用)时,可使用共用体节省空间。
* 匿名共用体没有名称,其成员将成为位于相同地址处的变量,每次只有一个成员是当前成员:
```C++
struct widget
{
char brand[20];
int type;
union // 匿名共用体
{
long id_num;
char id_char[20];
};
};
widget prize;
if (prize.type == 1) // id_num和id_char被认为是prize的两个成员,具有相同的地址,由程序员确定使用哪个成员
cin >> prize.id_num;
else
cin >> prize.id_char
```
#### 枚举
* `enum` 用于创建符号常量的方式,可代替`const`
#### 指针和自由存储空间
* 对于常规变量,其存储的是值,使用地址操作符(&)获取地址;指针是一种特殊数据类型的变量,其存储的是地址,使用接除引用操作符(*)获取对应地址存储的值。
* 声明与初始化指针
```C++
int *ptr; // c-风格指针声明
int* ptr; // c++风格指针声明,认为int*为一种类型
int* p1,p2; // p1为指向int的指针变量,p2为int类型常规变量,每一个指针变量都要有一个 *
int higgen = 5;
int * pt = &higgen; // 指针也是一种数据类型的变量,其存储的值为地址,因此初始化传递的应是地址
long * fellow;
*fellow = 223323; // 警告!!! 一定要在对指针应用解除引用操作符(*)之前,将指针初始化为一个确定,适当的地址!!
int * pt;
pt = (int *)OxB8000000; // 数字不能直接传递给指针,需要强制类型转换成地址类型 !!
```
* `new` 与 `delete`
* 使用`new`分配内存
```C++
typeName pointer_name = new typeName; // 数据类型可以是结构,或基本类型
int * pn = new int; // new会自动分配内存,并返回内存块的地址
```
* 使用`delete`释放内存
```c++
int * pt = new int;
delete pt; // 一定要配对使用new及delete,只能用于释放new分配的内存
int * ps = new int;
int * pq = ps;
delete pq; // delete使用关键在于,释放new分配的内存,即使用new时返回的内存块的地址,此地址也存储在指针pq中,因此可以对指针pq使用delete。警告!!!一般不要使两个指针指向同一内存块
```
* 使用`new`创建动态数组
1. 创建动态数组
```C++
int * psome = new int [10]; // new返回第一个元素的地址,并赋给指针psome
delete [] psome; // 释放内存
```
2. 使用动态数组
* 直接将指针当作数组名使用即可,此处数组与指针基本等价
* 可对指针进行加减操作,加一即指向下一个元素的地址,而数组名不可改变
```C++
int * p1 = new int [3];
p1[0] = 2;p1[1] = 5;p1[2] = 8;
p1 = p1 + 1; // 此时p1[0] = 5
```
3. `new` 与 `delete`使用规则
* 不要使用`delete`释放不是`new`分配的内存
* 不要使用`delete`释放同一内存块两次
* 如果使用`new []` 为数组分配内存,则应使用`delete []`来释放
* 如果使用`new []` 为一个实体分配内存,则应使用`delete `(没有方括号)来释放
* 对空指针应用`delete`是安全的
#### 指针,数组和指针算术
* 指针和数组基本等价的原因在于指针算术和C++内部处理数组的方式。整数常量加1,其值将加1;而指针加1,增加的量为它指向的类型的字节数(如指向int整型的指针加1,其增加的量为int整型对应的字节数,在计算机系统内存中,地址是以字节为单位递增的)。这也说明C++将数组名解释为地址。
* 多数情况下,C++将数组名解释为第一个元素的地址。
```C++
double wages[10];
double * pw = wages;
double * pw0 = &wages[0]; // 数组名即为第一个元素的地址
```
使用数组表示法时,C++都将执行以下转换:
`arrayname[i] 变成 *(arrayname + 1)`
* 指针与数组区别
* 指针的值可修改,数组名不可修改
* 对数组使用`sizeof()`时,返回的是数组长度(以字节为单位,数据类型对应字节X数组元素个数);而对指针使用`sizeof()`使,返回的是指针的长度。这种情况下,不会将数组名解释为地址!!
* 指针与字符串
* 在cout和多数C++表达式中,**char数组名**,**指向char的指针**以及用双引号括起来的**字符串常量**都被解释为字符串的第一个字符的地址。因此在使用cout时输出字符串,实际是接收字符串的第一个字符地址,然后不断打印直到检测到`\0`字符串结束符。
```C++
const char * p = "string"; // 合法,可以使用指针访问字符串
char * p = "string"; // 虽然编译通过,但实际禁止,不允许这种方式初始化指针!!
const string * p = "ABCD";
string * p = "ABCD"; // 这两种初始化指针方式都不对,因为字符串常量本身并不是string对象
string str = "ABCD";
string * p = &str; // 此种指针指向string对象
```
* 注意:不要使用字符串常量或未初始化的指针接收输入!
* `new`用于结构:创建结构及访问成员
```C++
struct inflatable
{
int price;
...
}; // 如之前一样,先定义结构类型inflatable
inflatable * ps = new inflatable; // 创建一个未命名的inflatable类型,并将其地址赋给一个指针
ps -> price; // 可用于访问结构的price成员
(*ps).price; // 同样使用解除引用操作符(*)加(.)访问成员
```
* C++管理数据内存的方式
1. 自动存储
在函数内部定义的常规变量使用自动存储,随着函数的调用自动产生,随着函数的结束自动释放。实际上,自动变量是一个局部变量。
2. 静态存储
使变量成为静态的方式:在函数外定义;使用关键字`static`声明变量.
3. 动态存储
使用`new`和`delete`。使得数据的生命周期不受函数或程序生存时间控制。
### chapter 5
#### for循环
* for循环语句:
```C++
for (initialization; test-expression; update-expression)
{
body
}
```
* for循环执行步骤:
1. 设置初始值。(初始部分可声明变量)
2. 执行测试表达式,查看循环是否要继续进行,为真继续进行循环。
3. 执行循环体操作。
4. 更新用于测试的值。
* 表达式与语句:
* 在C++中,每个表达式都有值。有时很明显,如`22 + 27`其值为49;有时不明显,如`x = 20`,赋值表达式也有值,其值为20。
* C++表达式是值或值与操作符的组合。
* 副作用(size effect):
判定表达式的值会改变内存中的数据的值时,称表达式有
副作用。如 x + 15会计算一个新的值,但不会改变x的值。不过对于 ++x + 15时,表达式就有副作用,因为x的值发送了改变。
* 表达式加分号就是C++有效的语句。
* 非表达式和语句:返回语句,声明语句和for语句都不满足“语句=表达式+分号”的格式。
* 递增操作符(++)和递减操作符(--)
* 前缀版本:++b,表示先将b的值加1,然后使用新的值来计算表达式。
* 后缀版本:a++,表示使用a当前的值计算表达式,然后再将a的值加1。
```C++
int x = 5;
int y = ++x; // 改变了x,然后赋值给y。x为6,y为6.
int z = 5;
int y = x++; // 先赋值给y,然后改变x。x为6,y为5.
```
* 副作用及顺序点:
* **副作用**指在计算表达式时对某些东西进行了修改。
* **顺序点**是程序执行的一个点,在这里,进入下一步之前确保对所有副作用进行评估。在C++中,分号就算一个顺序点。另外任何一个完整表达式都是一个顺序点。完整表达式的例子:表达式语句中的表达式部分以及用作while循环中检测条件的表达式。
```C
while (guest++ < 10)
printf("%d\n", guests); // 与C++中一致,如果guest为9,则因为while中表达式为一个顺序点,因此完成这个表达式之后guest加1,打印出来为10。要注意此种编程带来的影响!!!
```
* 语句块
* 使用`{}`包含多个语句
* 在语句块内声明的变量,生命周期为语句块内的范围,在语句块内创建,结束执行语句块后释放对应内存。
* 逗号操作符
* 允许一个语句包含多个表达式。
* 逗号操作符是一个顺序点,确保先计算第一个表达式,再计算第二个表达式。
* C++规定,逗号表达式的值为第二部分的值。如果包含多个表达式,就以最后一个表达式的值为逗号表达式的值。
#### 关系表达式
* 字符串的比较
* c-风格字符串不能使用形如`word == "mate"`(word为数组名)来进行字符串比较。因为实际上只是对地址的比较。而是使用函数`strcmp()`。
* `string`类字符串的比较,可直接使用关系操作符。不过需满足至少一个操作数为string类对象方可使用。
#### while 循环
* 循环语句:
```C++
while (test-condition) // 测试条件为真,则执行循环体
body
```
#### do while 循环
* 循环语句:
```C++
do
body
while (test-condition);
```
#### 循环和文本输入(后续添加)
* `cin`对象支持3种不同模式的单字符输入:
1. 使用原始的`cin`进行输入
```C++
char ch;
cin >> ch
while (ch != '#') // 设置‘#’为哨兵字符,以标志停止输入
{
... // 注意cin对象是从缓冲区读取字符,如果字符停止输入,但缓冲区仍有字符,则下次使用cin时会直接从缓冲区读取,而不是请求键盘输入
} // cin对象会忽略空白符及换行符!!!
```
2. 使用`cin.get(char)`接收任意字符
3. 注意`cin.get()`,`cin.get(char)`和`cin.get(name, arrsize)`,它们属于C++函数重载的oop特性,允许同名函数,但是参数列表不同。
#### 二维数组
* 二维数组是在一维数组的基础上构建,并不是C++内部提供的类型。
* 二维数组声明及初始化:
```C++
int maxtemps[4][5] =
{
{94, 98, 97, 103, 101},
{94, 98, 97, 103, 101},
{94, 98, 97, 103, 101},
{94, 98, 97, 103, 101}
};
```
#### 类型别名
* 使用预处理器:
`#define BYTE char` 这样,预处理器将在编译程序时用所有char替换所有的BYTE。
* 使用`typedef`创建别名:
```C++
typedef typeName aliasName;
typedef char byte; // 使用byte作为char的别名
```
typedef不会创建新类型,而是为已有类型建立一个新的名称。typedef 比 #define 能处理更复杂的类型别名。
### chapter 6
#### if语句及if-else语句
```C++
if (test-condition) // 测试条件为真,则执行语句
statement
if (test-condition)
statement1 // 如果多个语句, 则用花括号{}括起
else
statement2
if (test-condition)
statement1
else if (test-condition)
statement2
else
statement3
```
#### 逻辑表达式
* 逻辑或操作符(||)
* 逻辑与操作符(&&)
* 取值范围测试:不要使用形如`17 < age < 35`这种数学形式的表达式,其实际结果为`(17 < age) < 35`的逻辑值,应用完整逻辑表达式,如`(17 < age) && (age < 35)`。
* 逻辑非操作符(!)
#### switch语句
* 语句格式:
```C++
switch (integer-expression)
{
case label1: statement
...
default: statement
}
```
* 注意:
* integer-expression 必须为结果是整数值的表达式;每个标签必须是整数常量的表达式(如int, char类型及枚举量)。
* 程序跳转到switch特定代码时,将依次执行之后的语句,因此如果需要中止跳转则使用`break`,但是注意此处的`break`只能退出`switch`语句块,如果`switch`语句块包含在一个大循环中,无法使用`break`退出循环 !
* 无法用于浮点测试及两个变量的比较,因为case标签值要求为常量。
* if-else也可以用于多选项,但是当选项多于3个时,使用switch语句效率更高。
#### break和continue语句
* brak语句:跳过循环体剩余部分,并结束循环进入下一条语句。
* continue语句:跳过循环体剩余部分,并继续循环,开始下一轮循环。
#### 读取数字的循环
* 假设要将一系列数字读取入数组中
```C++
int n;
cin >> n;
```
如果输入的是一个单词而不是一个数字,发生类型不匹配时,将有四种情况:
* 已读入的内容 `n` 的值将保持不变。
* 不匹配的输入将被一直保留在输入队列中。
* `cin` 对象中的一个错误标记被设置。
* 对`cin`方法的调用将返回`false`(如果被转换为`bool`类型)。
* 对`cin`方法调用返回`false`意味着可以用非数字输入来结束读取数字的循环
* 非数字输入的设置错误标记意味着必须重置改标记,程序才能继续读取输入。调用`cin`的`clear()`方法来重置错误输入标记,同时也重置文件尾。
* `cin >> `的操作实际上也是`cin`方法函数的调用
* 当`cin`输入了错误内容,为了重新正常输入,应采取3个步骤:
1. 重置`cin`以接受新的输入。
2. 删除错误输入。
3. 提示用户再输入。
```C++
while (!(cin >> golf[i]))
{
cin.clear(); // 重置输入
while (cin.get() != '\n') // 将队列中的输入清除
continue;
cout << "提示输入";
}
```
#### 简单文件输入/输出(待补充)
### chapter 7
#### C++函数基本知识
* 使用C++函数前基本工作:
* 提供函数定义
* 提供函数原型
* 调用函数
* 函数定义
* ```C++
typeName func(paramlist)
{
statements
return value;
}
```
* `void` 函数无返回值函数
* 对于有返回值函数,必须使用返回语句将值返回给调用函数。值本身可以是常量,变量或表达式。
* 返回值的类型必须为声明的返回类型或可以被转换为typeName。不过C++对返回值类型有一定的限制,不能是数组,但可以是其他任何类型,包括结构对象以及包含数组元素的结构对象。
* 函数的返回结果会存储在一个`临时返回内存单元`内。
* 原型的功能
* 编译器正确处理函数返回值
* 编译器检查使用的参数数目是否正确
* 编译器检查使用的参数类型是否正确,如果不正确,则转换成正确的类型。
#### 函数参数与按值传递
* 函数的参数为形参,实际传递到函数的参数为实参
* 形参实际上是函数调用后自动创建的变量,伴随着函数的生命周期产生到消失
* 按值传递是指实参将其值复制拷贝给函数形参的过程
#### 函数与数组
* 函数头:`int sum_arr(int arr [], int n)` ,其中`int arr []`声明了传递的是数组参数,实际上 `arr` 为指针,声明等价为 `int * arr`
* 函数传递常规变量时,函数将使用该变量拷贝;但传递数组时,函数将使用原来的数组,不过函数形参接收数组指针地址,因此在函数内部应用`sizeof`时得到的是指针变量字节大小而不是原数组大小。
* 指针与 `const`:
* 使用 `const int *` 声明指针变量时,表示无法通过指针修改指向的变量,而不是指指针指向的变量为常量!
* 不能将常量地址赋值给常规指针,如 `char * p = "abc"` 是非法的,但是 `const char * p = "abc"` 合法。
* 总的说,如果数据类型本身并不是指针,则可以将const数据或非const数据的地址赋给const的指针,但只能将非const数据地址赋值给非const指针。
* 尽可能使用`const`,函数中应将指针形参声明为`const`指针!!
* 但是!如果函数形参为指向指针的指针或数组元素为指针,则不能使用 `const`。
#### 函数与二维数组
* 函数声明:
```C++
int sum(int (* arr2) [4], int size); // 两种声明一样
int sum(int arr2[][4], int size);
```
* 二维数组的数组名实际上为指向向指针的指针。其作为函数形参不使用`const`。
* 以`int arr2[3][4]`为例解析二维i数组数组名:
* `arr2` 表示指向第一行的4元素的数组指针
* `arr2 + r` 表示第 r+1 行4元素数组的指针
* `*(arr2 + r)` 表示第 r + 1 行数组名,也就是第 r + 1 行第一个元素的地址
* `*(*(arr2 + r) + c)` 表示第 r + 1 行数组第 c + 1 个元素。即 `arr2[r][c]`
#### 函数和C—风格字符串
#### 函数和结构
## 未完待续 . . .