C++primer 习题知识点 1-7章

本文详细解析了C++ Primer中1-7章的重要知识点,涵盖for循环与while循环的对比、C++的数据类型、指针与引用的区别、字符串与向量操作、数组、表达式和语句的基础知识。内容包括变量初始化、类型检查、常量指针与引用、字符串的读写、vector的声明与操作、数组拷贝及函数参数传递等核心概念。
摘要由CSDN通过智能技术生成

第一章 开始

1. 对比for循环和while循环,两种形式的优缺点各是什么?

在for循环中,循环控制变量的初始化和修改都放在语句头部分,形式较简洁,且特别适用于循环次数已知的情况

在while循环中,循环控制变量的初始化一般放在while语句之前,循环控制变量的修改一般放在循环体中,形式上不如for语句简洁,但它比较适用于循环次数不易预知的情况(用某一条件控制循环)。

两种形式各有优点,但它们在功能上是等价的,可以相互转换。

 

【术语】:

【类(class)】一种用于定义自己的数据结构及其相关操作的机制。

【数据结构(data structure)】数据及其上所允许的操作的一种逻辑组合。

【头文件(header)】使类或其他名字的定义可被多个程序使用的一种机制。

C++是一种静态类型(statically typed)语言,其含义是在编译阶段检查类型。其中,检查类型的过程称为类型检查(type checking)。

 

第二章 C++基础

1. C++ guarantees short and int is at least 16 bits, long at least 32 bits, long long at least 64 bits.

The signed can represent positive numbers, negative numbers and zero, while unsigned can only represent numbers no less than zero.

The C and C++ standards do not specify the representation of float, double and long double. It is possible that all three implemented as IEEE double-precision.

2. 首先了解一下字符串的前后缀:

 

前缀:u:unicode16字符;U:unicode32字符;L:宽字符;u8:utf-8;

后缀:u或U:表示该字面值为无符号类型; l或L:表示该字面值的类型至少为long

ll或LL:表示该字面值的类型至少为long long ; f或F:表示该字面值为float类型

前后缀可以交叉结合使用:后缀UL时,表示无符号长整型。

字符用单引号‘’,字符串用双引号“”。

十进制:20   八进制:020    十六进制:0x20或者0X20

 

3. 默认初始化的三条性质:

(1)定义在任何函数体外的变量会被初始化为0。

(2)定义在函数体内部的变量不会被初始化。

(3)类的对象未被初始化,则初值由类决定。

 

4. 声明:使得名字为程序所知,如果想使用该变量,则必须实现包含其声明。声明会确定变量的名字和类型。定义:创建于名字关联的实体。定义会申请存储空间,可能会赋予初始值。如果想声明一个变量而不想定义它,那么可以在变量名前加extern关键字。

 

5. 关于命名:字母+数字+下划线(必须以字母或者下划线开头)

C++中关于变量命名的规范:

1:能体现变量的实际意义

2:变量名一般采用小写字母

3:用户自定义变量名可以以大写字母开头

4:多个单词定义的变量名应有区分Student_loan   而不是  Studentloan

 

6. 指针与引用

int i =10;

int &j = i;      //&符号紧随类型名出现,它是声明的一部分,j是一个引用。

int *p;        //*符号紧随类型名出现,它是声明的一部分,p是一个指针。

p = &i;        //&符号出现在表达式之中,是一个取地址符号。

*p = i;        //*符号出现在表达式之中,是一个解引用符

int &j2 = *p;   //&是声明的一部分,*是解引用符。

指针与引用的区别:指针本身就是一个对象,允许指针之间的拷贝和赋值,也可以在其生命周期指向不同的对象;而引用不行。指针无需在定义时赋值。

 

7. 给定指针p,不能知道其是否指向了一个合法的对象;因为不知道指针是否有效。

 

8. void定义了一个空指针,可以接受任意类型的对象

 

9. 指针与const:(1)指向常量的指针:要想存放变量的地址,必须使用指向常量的指针:const int *p;(2)常量指针:指针自身是一个对象,不可变。int *const p = &pi   (常量指针必须初始化!!!);(3)指向常量的常量指针。const  int *const p (不仅指针本身不可变,指针所指对象也不可变)。

 

10. 顶层const(top-level const)表示指针本身是个常量;底层const(low-level const)表示指针所指的对象是一个常量。顶层const:指的是本身不可以被改变;底层const:指的是自身所指对象不可以被改变。用于声明引用的const都是底层const。

 

11. 使用类型别名:C++中可以定义一个类型的别名,有两种方式:(1)使用typedef;(2)使用别名名称。

 

12. auto 类型说明符:当我们不知道变量表达式的类型时,可以使用auto让编译器为我们决定表达式的类型。auto会自动忽略掉顶层const,而底层const会被保留。auto定义的变量必须有初始值

 

13. decltype(())双层括号表示引用(注意引用必须初始化)。赋值的表达式语句本身是一种引用。Auto指定类型与decltype指定类型的区别在于:如果使用引用类型,auto会识别为其所指对象的类型,decltype则会识别为引用的类型。

 

【术语】

【常量指针(const pointer)】是一种指针,它的值永不改变。

【常量引用(const reference)】含义是指向常量的引用。

【常量表达式(const expression)】能在编译时计算并获得结果的表达式。constexpr是一种函数,用于代表一条常量表达式。

【指向常量的指针(pointer to const)】是一个指针,存放着某个常量对象的地址。指向常量的指针不能用来改变他所指对象的值。

 

第三章 字符串、向量和数组

1. 标准库类型String

(1)string对象的定义和初始化

(2)string对象的读写

对:cin>>s;从标准输入读取string,并将读入的串存储在s中。

a.读取并忽略开头所有的空白字符(如空格,换行符,制表符,进纸符);

b.读取字符直至再次遇到空白符,读取终止;

 

(3)string对象的操作

a. string::size_type 类型

注意s.size()返回的类型是string::size_type()类型,而不是int形,string::size_type的类型长度是int的俩倍所以尽量让s.size()的返 回值给string::size_type 类型;因为string 的字符会很长,所以有了string::size_type 类型。

b.和字符串字面值的连接问题

当进行string对象和字符串字面值进行连接时,+操作符的左右至少出现一个是string类型的。

(4)string对象中字符的处理(在cctype头文件中被定义)

(5)string的遍历

(6)构造string对象的其他方法

a.使用只有一个指针参数的构造函数,该指针指向空字符结束的字符数组中的第一个元素。

b.构造函数需要一个指向字符数组元素的指针和一个标记要复制多少字符的计数器做参数,由于该构造函数有一个计数器,因此  数组不必以空字符结束。

(7)修改string对象的其他方法

(8)只适用于string类型的操作

a. substr函数,返回当前string对象的子串

b. append和replace函数,用于修改string对象

c. 一系列find函数,用于查找string对象

(9)string类型的查找操作

string类提供了6中查找函数,每种函数以不同形式的find命名。这些操作全都返回string::size_type类型的值,以下标形式标记查找所发生的位置;或者返回一个名为string::npos 的特殊值,说明没有找到匹配。string类将npos定义为保证大于任何有效下标的值。

 

(10)string对象的比较

 

2. 标准库类型vector(头文件:#include<vector>)

(1)简介

  1. vector是表示可变大小数组的序列容器。
  2. 就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。
  3. 本质讲,vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大小。
  4. vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。
  5. 因此,vector占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增长。
  6. 与其它动态序列容器相比(deques, lists and forward_lists), vector在访问元素的时候更加高效,在末尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。比起listsforward_lists统一的迭代器和引用更好。

(2)vector声明及初始化

vector<int> vec;        //声明一个int型向量

vector<int> vec(5);     //声明一个初始大小为5的int向量

vector<int> vec(10, 1); //声明一个初始大小为10且值都是1的向量

vector<int> vec(tmp);   //声明并用tmp向量初始化vec向量

vector<int> tmp(vec.begin(), vec.begin() + 3);  //用向量vec的第0个到第2个值初始化tmp

int arr[5] = {1, 2, 3, 4, 5};  

vector<int> vec(arr, arr + 5);      //将arr数组的元素用于初始化vec向量

//说明:当然不包括arr[4]元素,末尾指针都是指结束元素的下一个元素,

//这个主要是为了和vec.end()指针统一。

vector<int> vec(&arr[1], &arr[4]); //将arr[1]~arr[4]范围内的元素作为vec的初始值

 

注意:

1:引用不可以成为vector的元素,因为其不是对象。

2:可以用花括号初始化每一个值。

3:可以用括号指定元素个数或相同的元素值。

4:只能使用直接初始化,不可以使用拷贝初始化(vector之间的拷贝是可行的,但要保证类型相同)

 

(3)vector基本操作

1). 容量

向量大小: vec.size();

向量最大容量: vec.max_size();

更改向量大小: vec.resize();

向量真实大小: vec.capacity();

向量判空: vec.empty();

减少向量大小到满足元素所占存储空间的大小: vec.shrink_to_fit(); //shrink_to_fit

2). 修改

多个元素赋值: vec.assign(); //类似于初始化时用数组进行赋值

末尾添加元素: vec.push_back();

末尾删除元素: vec.pop_back();

任意位置插入元素: vec.insert();

任意位置删除元素: vec.erase();

交换两个向量的元素: vec.swap();

清空向量元素: vec.clear();

3)迭代器

开始指针:vec.begin();

末尾指针:vec.end(); //指向最后一个元素的下一个位置

指向常量的开始指针: vec.cbegin(); //意思就是不能通过这个指针来修改所指的内容,但还是可以通过其他方式修改的,而且指针也是可以移动的。

指向常量的末尾指针: vec.cend();

4)元素的访问

下标访问: vec[1]; //并不会检查是否越界

at方法访问: vec.at(1); //以上两者的区别就是at会检查是否越界,是则抛出out of range异常

访问第一个元素: vec.front();

访问最后一个元素: vec.back();

返回一个指针: int* p = vec.data(); //可行的原因在于vector在内存中就是一个连续存储的数组,所以可以返回一个指针指向这个数组。这是是C++11的特性。

(4)算法

  1. 遍历元素

vector<int>::iterator it;

for (it = vec.begin(); it != vec.end(); it++)

    cout << *it << endl;

//或者

for (size_t i = 0; i < vec.size(); i++) {

    cout << vec.at(i) << endl;

}

  1. 元素翻转

#include <algorithm>

reverse(vec.begin(), vec.end());

  1. 元素排序

#include <algorithm>

sort(vec.begin(), vec.end()); //采用的是从小到大的排序

//如果想从大到小排序,可以采用上面反转函数,也可以采用下面方法:

bool Comp(const int& a, const int& b) {

    return a > b;

}

sort(vec.begin(), vec.end(), Comp);

 

3. 关于数组的拷贝,不可以将整个数组赋值给另一个数组,只能逐个元素的拷贝。如果利用vector,可以直接将整个vector拷贝给另一个vector。另外vector不可以便利的像数组那样直接花括号定义(貌似新版支持了~)

 

【术语】

【缓冲区溢出(buffer overflow)】一种严重的程序故障,主要的原因是试图通过一个越界的索引访问容器内容,容器类型包括string、vector和数组等。

【C风格字符串(C-style string)】以空字符结束的字符数组。字符串字面值是C风格字符串,C风格字符串容易出错。

 

第四章 表达式

1. 运算符优先级问题:主要记住小括号的利用、算术>关系>逻辑

2. 溢出的定义:当计算的结果超出了该类型所能表示的最大范围时就会产生溢出。

 

第五章 语句

1.空语句:最简单的语句,当程序中在语法上需要一条语句,但是逻辑上却不需要时,就可以用到空语句。

2. 悬垂else:C++规定,else与其最近的尚未匹配的if相匹配;

3. 关于continue:

知识点1:continue语句:终止最近循环中的当前迭代并立即开始下一次迭代。

知识点2:它只能出现在for、while、do while的内部。

4. 异常

知识点1:异常——指运行时程序的反常行为,典型的有:失去数据库连接、遇到意外的输入等。

知识点2:异常处理—— 在异常被检测出以后,进行异常的处理,主要分为三种:throw表达式、try语句快和异常类

throw表达式:异常检测部分使用throw表达式来表示它遇到的无法处理的问题。

try语句块:处理异常,以关键字try开始,以一个或多个catch子句结束。try语句块中代码抛出的异常通常会被catch子句处理。被称为异常处理代码。

异常类:用于在throw和相关的catch子句之间进行异常信息的传递。

 

 

第6章 函数

1. 对于实参和形参的区别,可以简单的理解为:

形参:函数定义的时的参数—也就是无赋值的变量(作用是说明参数的类型)。

实参:调用函数时使用的参数—也就是有赋值的变量(函数实际操作的对象。

 

2. 函数基础

知识点1:函数包括:返回类型、函数名、参数。调用函数的过程:第一步:实参初始化函数对应的参数,第二步:将控制权转移给被调函数。

知识点2:实参的类型必须与对应的形参相匹配(可以进行类型转换,如:double转为int)

 

3. 形参、局部变量以及局部静态变量。

知识点1:局部变量:形参和函数体内部定义的变量统称为局部变量(注意其作用域)

知识点2:局部静态对象:直到程序的结束才被销毁的对象,不受块的约束

 

4. 分离式编译

C++允许我们将程序分割到几个文件中去,每个文件独立编译。这里可以将函数的定义放到.cc文件中,main()函数放到另一个.cc文件中。

 

5. void f(T)以传值方式传入参数,不能修改实参。Void f(&T)以传址的方式传入参数,可以修改实参。

 

6. 引用是地址传值,作为引用的形参数值被修改的同时,也修改了对应实参的值。你不用引用当然可以,只是实参的值不会随着形参被修改。引用还有另外一个作用,声明这个变量的时候不会浪费额外的内存空间,对引用的形参的操作实际就是对实参的操作。

 

7. 形参的类型决定了形参和实参交互的方式。如果形参是引用类型,它将绑定到对应的实参上;否则,将实参的值拷贝后赋给形参。引用类型时,我们说其对应的实参被引用传递或者函数被传引用调用。当实参的值被拷贝给形参时,形参和实参是两个相互独立的对象,我们说这样的实参被值传递或者函数被传值调用

 

8.

知识点1:数组的两个特殊点:(1)不允许拷贝数组,所以不可以使用传值的方式使用数组参数(传值即为拷贝);(2)通常数组的传递使用的是指针形式,传递的是指针的首地址。

知识点2:由于数组是以指针的形式传递给函数的,所以函数不知道数组的大小,指针就可能会发生越界问题。有三种方法可以解决这一点问题。

  1. 数组的末尾如果存在结束标记(数组存的是C风格的字符串时,末尾为结束符、空白符);
  2. 使用标准库范围,即将数组的首指针和末尾指针都传入函数;
  3. 显示的传递数组的大小,将数组大小作为一个参数传入函数。

知识点3:当函数不需要对数组元素进行写操作时,使用const常量指针。

知识点4:引用的数组和数组的引用是有区别的!数组的引用才可以作为函数的参数。

知识点5:多维数组的传递,传递的同样是数组的首元素,不过多元数组的首元素本身就是数组。

 

9. 返回的引用无效:局部临时变量或者局部对象的引用对于返回都是无效的,因为在函数终止之后,局部变量或者对象的引用不再指向有效的内存区域。若是常量在函数调用之前存在,引用即可用。

 

10. 由于数组不能被拷贝,所以函数不可以返回数组,但是我们可以返回函数的指针。利用的是类型别名的方法:①声明返回数组指针的函数;②尾置返回类型;③使用decltype,已知函数的返回值时,可以使用关键字decltype表示返回类型为指针。

 

11. ①函数的重载必须有形参数量或者形参类型上的不同;②顶层const不影响传入函数的对象

 

12. 一旦函数的某个形参被赋予了默认值,他后面所有的参数都必须有默认值

 

13.①函数反复调用的过程中重复出现的形参,这样的值被称为默认实参。该参数在使用过程中可以被用户指定,也可以使用默认数值;

②一旦函数的某个形参被赋予了默认值,他后面所有的参数都必须有默认值;

③调用含有默认实参的函数时,可以包含该实参,也可以省略该实参。

④顺序很重要!在设计函数时,将默认值的形参放在后面。

⑤在给定的作用域中,一个形参只能被赋予一次默认实参,且局部变量不能作为默认实参。

 

14. ①将函数指定为“内联函数(inline)”,将它在每个调用点上“内联的展开”,该说明只是向编译器发出一个请求,编译器可以选择忽略这个请求。内联的机制用于优化规模较小、流程直接、频繁调用的函数,建议不大于75行。

②constexpr函数是指能用于常量表达式的函数:函数的返回值类型和所有形参的类型必须是“字面值类型”:算术、引用、指针。并且函数体内有且只有一条return语句。

③将较小的操作如比较两个字符串的大小定义为函数,有很多的优点。

④inline函数和constexpr函数可以在函数中多次定义,但是通常将其定义在头文件中。

 

15. ①预处理宏assert(expr):包含一个表达式,expr为真时,assert什么也不做,为假时输出信息并终止程序。包含在cassert头文件中。通常用于检查不能发生的条件;

②assert依赖于一个NDEBUG的预处理变量的状态,如果定义了NDEBUG,assert什么也不做,默认状态下NDEBUG是未定义的。编译器也可以预先定义该变量。

③也可以使用NDEBUG编写自己的条件调试代码,如果NDEBUG未定义,将执行#ifndef到#endif之间的代码,如果定义了NDEBUG,这些代码将被忽略。

 

16. 候选函数:函数匹配的第一步是选定本次调用的重载函数集,集合中的函数被称为候选函数,具备两个特征:①是与被调用的函数同名,②是其声明在调用点可见。根据实参情况,从候选函数中挑选出能被这实参调用的函数,此次选出的函数被称为可行函数函数匹配:重载函数调用时的选择。

 

17.

①函数指针指向的是函数并非对象。要声明一个指向函数的指针只要用指针替代函数名即可;

②当我们把函数名当作一个值使用时,函数自动的转换为指针,直接赋予或者取址皆可。可以直接使用只想该函数的指针调用该函数;

③给指针赋予nullptr或者0时,指针不指向任何函数;

④函数重载时,指针的类型必须与重载函数精确匹配,包括形参类型数量和返回值类型;

⑤虽然不能返回一个函数,但是可以返回一个指向函数的指针。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值