c++面试经验(可下载文档)

                                           **c++面试宝典**

本文总结的文档可在以下网址下载:
https://download.csdn.net/download/qq_36549611/14906081
以下是一些面试问题的网站:
网站:
https://www.nowcoder.com/tutorial/93/675fd4af3ab34b2db0ae650855aa52d5
https://www.cnblogs.com/inception6-lxc/p/8686156.html
https://www.nowcoder.com/ta/review-c?query=&asc=true&order=&tagQuery=&page=1
https://github.com/huihut/interview#data-structure
https://blog.nowcoder.net/n/f73dc0635ae14a7e8643a9dba9b5783c
写在前面:
找工作和面试都需要提前准备,我是在研二下班学期开始准备的,大概4月份开始,当时就已经有春招或者实习生的招聘。春招主要是针对应届生,所以我投了一些公司的实习招聘。当然由于自己当时还不知道怎么准备,所以投的招聘笔试和面试基本都凉了。随后到暑假7,8月份开始投一些公司提前批,还在忙课题,找工作没有准备太多,所以浪费了很多好公司的招聘,当时准备非常不充分,问的问题都答不上来,所以尽早把自己的课题弄好,才好把更多的时间留给找工作。9,10月份投正式批,那时候学校开学,也会有公司陆陆续续来学校宣讲,这两个月就专心找工作,一天到晚不是笔试面试,就是投简历准备面试的知识,一般到10月底,大部分都会有offer,可能是在选择当中要不要签。11月以后会陆续有一些补录的公司,这个要看每个公司的发展,像小米,新浪,讯飞等公司,之前投了没有消息,他们人没有招够就会从之前投的人中筛选。总之一句话就是广投,广面,积累经验和总结问题。
笔试的时候可以招人帮忙查题,但是面试最多还是要靠自己,可以在牛客或者力扣网上刷题来提高自己的代码能力,然后就是一些基本的知识,面试会问很多问题,我也只是总结了比较常见的。
流程:
(1)投简历,自己的简历是比较重要的,这关系到面试官能否一眼看中你,可以参考之前师兄师姐的简历然后修改,即使是自己没有参与的项目,只要能够讲清楚弄明白也可以写上。多关注一些公众号,可以在牛客上查看招聘信息,有的公司有内推,也可以在官网投递。
(2)测评和笔试。测评指的是一些性格测评或者类似于阅读短文主旨,看图表找信息的,可以在网上找一些攻略。笔试一般就是选择或者敲代码,很多都是力扣和牛客的原题或者类似的题。
(3)面试。分为三面,第一面是基础面,主要是考察对编程的基本内容,包括方方面面,也可能简单问一下项目。 第二面是项目面,主要问项目,但也会问一些基础知识。第三面是HR面,主要就是谈一些薪资待遇,工作城市之类的,还有性格问题。一般到了第三面基本问题就不大了。
(4)发放offer。面试通过会给你发送offer,在没有拿到三方之前,可以先签两方。拿到三方,对公司也比较满意,可以直接签一个保底,再往后如果遇到更好的可以毁约。
在这里插入图片描述

推荐使用类似OneNote或者其他的记录形式,将自己的学习和面试的经验问题给一一记录下来。
简历建议:
1,内容多可以做两页,获奖少,发表论文和项目少做一页也可以。
2,邮箱尽量不要用QQ和谷歌,可以用网易126或163
3,每个技能证书都要弄清楚作用
4,自我评价可以从多方面来具体罗列,比如学术(发表论文),英语(证书),组织能力(参加活动),编程能力(复现)等
5,简历要备用多份,以备不同类型的公司。比如国企比较注重活动,可以把校内外活动加上,私企注重专业技能,着重写专业技能,不写校内外活动。(具体要求需上网查看)
6,教育经历要么不写课程,成绩好的话尽量把自己得分高的主修课程写上,并且注上分数
7,项目经历主要写自己的任务(比如复现或者创新)和所得成果,不要累赘
8,技能要求注意层次,主要,熟悉,掌握,了解等区别,也要注意所投岗位或者公司的要求
9,项目经历时间点要连贯,可以重复,但不能空白
10,简历7分真,3分假
11,简历可以多做,比如投的简历一页,做简历做两页,面试一次要改一次
12,简历不要给自己挖坑,不要写自己不了解的知识
一,自我介绍
其实就是将简历上的内容给大致念下来,带着面试官去过一遍你的简历,不用每一项都说,但是要把重点和你的亮点说出来。
1,自我介绍就是把简历给念一遍,稍加补充,引导面试官更清楚简历
2,学校的专业排名着重提 B+
3,要把自己的求知欲,职业规划表达出来,不超过3分钟
4,可以把自己的专业知识简单介绍,以及考研的追求(不是从众)
5,言简意赅,时间控制在规定时间内
6,自己的经历和应聘要求相关的有哪些
7,为什么你能够胜任这个岗位,(3个优势和两点)
8,为什么要应聘这个岗位(岗位理解和职业规划)
项目中用到的c++知识:可以在网上自己找小项目做,将C++的重要知识亲自用一遍
1,封装:个体类,种群类
2,继承:个体和种群有共同的属性,比如位置和速度,可以继承
3,多态:(静态多态,函数重载)分层学习时调用初始化函数,初始化的数目不同,没有动态多态
4,拷贝构造函数(散开算子重新定义对象进行比较)
5,STL库,函数库(sort),vector,stack,map,set
在这里插入图片描述

面试主要围绕以上几个方面,其中5和8不是必须会的,我之前也没有学过数据库和设计模式,但是可以作为了解,当做加分项目。

二,C++知识:

1,动态申请函数new和malloc.
在这里插入图片描述

malloc需要给定申请内存的大小,返回的指针需要强转。
new会调用构造函数,不用指定内存大小,返回的指针不用强转。
2,进程和线程的区别 ,进程间的通信
在这里插入图片描述
在这里插入图片描述

进程间通信方式:

线程间通信方式为共享内存和消息队列。
3,const和static
Const:修饰常量:定义时候初始化,以后不能更改
修饰形参:该形参在函数里不能改变
修饰类成员函数:该函数对成员变量只能进行只读操作。
(1)当static用来修饰局部变量的时候,它就改变了局部变量的存储位置(从原来的栈中存放改为静态存储区)及其生命周期(局部静态变量在离开作用域之后,并没有被销毁,而是仍然驻留在内存当中,直到程序结束,只不过我们不能再对他进行访问),但未改变其作用域
(2)static修饰全局变量,并未改变其存储位置及生命周期,而是改变了其作用域,使当前文件外的源文件无法访问该变量,好处如下:(1)不会被其他文件所访问,修改(2)其他文件中可以使用相同名字的变量,不会发生冲突。
(3)用static修饰类的数据成员实际使其成为类的全局变量,会被类的所有对象共享,包括派生类的对象。因此,static成员必须在类外进行初始化(初始化格式: int base::var=10;),而不能在构造函数内进行初始化,不过也可以用const修饰static数据成员在类内初始化 。
(4)用static修饰成员函数,使这个类只存在这一份函数,所有对象共享该函数,不含this指针
(5)static修饰普通函数,表示这个函数只能在当前文件中被访问

不可以同时用const和static修饰成员函数,可以同时修饰成员变量。C++编译器在实现const的成员函数的时候为了确保该函数不能修改类的实例的状态,会在函数中添加一个隐式的参数const this*。但当一个成员为static的时候,该函数是没有this指针的。也就是说此时const的用法和static是冲突的
(1)静态常量数据成员可以在类内初始化(即类内声明的同时初始化),也可以在类外,即类的实现文件中初始化,不能在构造函数中初始化,也不能在构造函数的初始化列表中初始化;
(2)静态非常量数据成员只能在类外,即类的实现文件中初始化,也不能在构造函数中初始化,不能在构造函数的初始化列表中初始化;
(3)非静态的常量数据成员不能在类内初始化,也不能在构造函数中初始化,而只能且必须在构造函数的初始化列表中初始化;
(4)非静态的非常量数据成员不能在类内初始化,可以在构造函数中初始化,也可以在构造函数的初始化列表中初始化
因为静态成员属于整个类,而不属于某个对象,如果在类内初始化,会导致每个对象都包含该静态成员,这是矛盾的。
能在类中初始化的成员只有一种,那就是静态常量成员

4,野指针
指针:为什么要赋初值,不赋初值造成什么什么影响?
指针在被定义的时候,如果程序不对其进行初始化的话,它会随机指向一个区域,因为任意指针变量(除了static修饰的指针)它的默认值都是随机的,会出现野指针。
野指针指向了一块随机内存空间,不受程序控制。如指针指向已经被删除的对象或者指向一块没有访问权限的内存空间,之后如果对其再解引用的话,就会出现问题。
野指针的问题在于,指针指向的内存已经无效了,而指针没有被置空,解引用一个非空的无效指针是一个未被定义的行为,也就是说不一定导致段错误,野指针很难定位到是哪里出现的问题,在哪里这个指针就失效了,不好查找出错的原因。所以调试起来会很麻烦,有时候会需要很长的时间。
在这里插入图片描述

在这里插入图片描述

5,继承的方式
在这里插入图片描述

6,堆和栈的区别

在这里插入图片描述
在这里插入图片描述

7,智能指针
智能指针主要用于管理在堆上分配的内存,它将普通的指针封装为一个栈对象。当栈对象的生存周期结束后,会在析构函数中释放掉申请的内存,从而防止内存泄漏。
智能指针的作用是管理一个指针,因为存在以下这种情况:申请的空间在函数结束时忘记释放,造成内存泄漏。使用智能指针可以很大程度上的避免这个问题,因为智能指针就是一个类,当超出了类的作用域,类会自动调用析构函数,析构函数会自动释放资源。所以智能指针的作用原理就是在函数结束时自动释放内存空间,不需要手动释放内存空间。
8,拷贝构造函数
在这里插入图片描述
在这里插入图片描述

9,大端小端
所谓的大端模式,是指数据的高字节,保存在内存的低地址中,而数据的低字节,保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加,而数据从高位往低位放。
在这里插入图片描述

所谓的小端模式,是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中,这种存储模式将地址的高低和数据位权有效地结合起来,高地址部分权值高,低地址部分权值低,和我们的逻辑方法一致
在这里插入图片描述

10,纯虚函数和虚函数的区别,为什么有了虚函数还要有纯虚函数?
虚函数是允许被其子类重新定义的成员函数。
为什么:在有些情况下,在基类中不能对虚函数给出有意义的实现,而把它说明为纯虚函数,他的实现留给该类的子类去做。
纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。在基类中实现纯虚函数的方法是在函数原型后加"=0"
区别:虚函数为了重载和多态的需要,在基类中是有定义的,即便定义是空,所以子类中可以重写也可以不写基类中的此函数!
纯虚函数在基类中是没有定义的,必须在子类中加以实现
(1)虚函数和纯虚函数可以定义在同一个类(class)中,含有纯虚函数的类被称为抽象类(abstract class),而只含有虚函数的类(class)不能被称为抽象类
(2)虚函数可以被直接使用,也可以被子类(sub class)重载以后以多态的形式调用,而纯虚函数必须在子类(sub class)中实现该函数才可以使用,因为纯虚函数在基类(base class)只有声明而没有定义。

纯虚函数,抽象类,接口
在这里插入图片描述

11封装,继承,多态
封装:在C++中,比较狭隘的解释就是将数据(成员变量)与操作数据的方法(成员函数)放在一个类中,而后给每个成员设置相应的权限。从大一点的角度来说,封装就是将完成一个功能所需要的所有东西放在一起,对外部只开放调用它的接口。
继承:继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行拓展。其继承的过程,就是从一般到特殊的过程。
多态: 多态简单的说就是“一个函数,多种实现”,或是“一个接口,多种方法”。指相同对象收到不同消息或不同对象收到相同消息时产生不同的实现动作。
C++支持两种多态性:编译时多态性,运行时多态性。
a.编译时多态性(静态多态):通过重载函数实现
C++允许在同一作用域中声明几个类似的同名函数,这些同名函数的形参列表(参数个数,类型,顺序)必须不同,常用来处理实现功能类似数据类型不同的问题。
b 运行时多态性(动态多态):通过虚函数实现。
12多个智能指针指向同一对象,会有什么情况?
会造成内存泄露。对象A想释放自身的资源的时候,发现B的对象还存在,没办法释放掉成员B的资源,因此A等待B先释放,同样,B在释放的时候也需要等待A的资源先释放掉,这样相互等待导致内存泄漏

13 c++11新特性
(1)初始化列表:当成员是const类型,成员是引用类型,有一个成员是类的对象时候,需要初始化列表。
(2)Auto自动类型转换
(3)Nullptr代替了NULL
(4)智能指针
(5)多线程
在C++11以前,C++的多线程编程均需依赖系统或第三方接口实现,一定程度上影响了代码的移植性。C++11中,引入了boost库中的多线程部分内容,形成C++标准,形成标准后的boost多线程编程部分接口基本没有变化,这样方便了以前使用boost接口开发的使用者切换使用C++标准接口,把容易把boost接口升级为C++接口
(6)正则表达式
14 哈希的底层实现
HashMap底层是数组和链表的结合。HashMap通过key的HashCode经过扰动函数处理过后得到Hash值,然后通过位运算判断当前元素存放的位置,如果当前位置存在元素的话,就判断该元素与要存入的元素的hash值以及key是否相同,如果相同的话,直接覆盖,不相同就通过拉链法解决冲突,把新元素添加到链表中。当Map中的元素总数超过Entry数组的0.75时,触发扩容操作,为了减少链表长度,提高访问速度,使元素分配更均匀。
15 vector和list区别?
1)vector的底层结构是动态顺序表,在内存中是一段连续的空间。
list的底层结构是带头节点的双向循环链表,在内存中不是一段连续的空间。
(2)vector支持随机访问,可以利用下标精准定位到一个元素上,访问某个元素的时间复杂度O(1)。
list不支持随机访问,要想访问list中的某个元素只能是从前向后或从后向前依次遍历,时间复杂度是O(N)。
(3)vector任意位置插入和删除的效率低,因为它每插入一个元素(尾插除外),都需要搬移数据,时间复杂度是O(N),而且插入还有可能要增容,这样一来还要开辟新空间,拷贝元素,是旧空间,效率会更低。
list任意位置插入和删除的效率高,他不需要搬移元素,只需要改变插入或删除位置的前后两个节点的指向即可,时间复杂度为O(1)。
(4)vector由于底层是动态顺序表,在内存中是一段连续的空间,所以不容易造成内存碎片,空间利用率高,缓存利用率高。
list的底层节点动态开辟空间,节点容易造成内存碎片,空间利用率低,缓存利用率低。
(5)vector适合需要高效率存储,需要随机访问,并且不管行插入和删除效率的场景。
list适合有大量的插入和删除操作,并且不关心随机访问的场景

16,内联函数和构造函数为何不能为虚函数
在这里插入图片描述

17深拷贝和浅拷贝的区别
浅拷贝:如果没有显示声明一个拷贝构造函数,系统将会自动生成默认拷贝构造函数,默认拷贝构造函数即位拷贝,又称浅拷贝
深拷贝:在对含有指针成员的对象进行拷贝时,必须要自己定义拷贝构造函数,使拷贝后的对象指针成员有自己的内存空间,即进行深拷贝,这样就避免了内存泄漏发生。
深拷贝:复制过程中资源重新分配
浅拷贝:资源不重新分配 ----
浅拷贝的风险:进行复制过程中,两个对象指向同块内存,当析构函数释放内存时,会引起错误
深拷贝和浅拷贝可以简单理解为:如果一个类拥有资源,当这个类的对象发生复制过程的时候,资源重新分配,这个过程就是深拷贝,反之,没有重新分配资源,就是浅拷贝
18 c和c++的区别
(1)强制类型转换
(2)变量可以在使用的时候定义,不用在一开始
(3)引入命名空间namespace
(4)函数重载:函数重载是函数的一种特殊情况,指在同一作用域中,声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数、类型、顺序)必须不同,返回值类型可以相同也可以不同,常用来处理实现功能类似数据类型不同的问题。(C语言没有函数重载,C++支持函数重载)。
(5)New delete和 malloc free
(6)面向过程和面向对象(蛋炒饭)
面向过程:
当需要实现一个功能的时候,每一个步骤我们都需要自己去做,处理实现功能的每一个细节。
面向对象:
当需要实现一个功能的时候,我们不需要自己去做,可以直接找一个已经具有该功能的东西,来帮我解决问题。举个例子:(玩五子棋)
面向过程的设计思路就是首先分析问题的步骤:1、开始游戏,2、黑子先走,3、绘制画面,4、判断输赢,5、轮到白子,6、绘制画面,7、判断输赢,8、返回步骤2,9、输出最后结果。把上面每个步骤用分别的函数来实现,问题就解决了。
面向对象的设计则是从另外的思路来解决问题。整个五子棋可以分为 1、黑白双方,这两方的行为是一模一样的,2、棋盘系统,负责绘制画面,3、规则系统,负责判定诸如犯规、输赢等。第一类对象(玩家对象)负责接受用户输入,并告知第二类对象(棋盘对象)棋子布局的变化,棋盘对象接收到了棋子的i变化就要负责在屏幕上面显示出这种变化,同时利用第三类对象(规则系统)来对棋局进行判定。
比如我要加入悔棋的功能,如果要改动面向过程的设计,那么从输入到判断到显示这一连串的步骤都要改动,甚至步骤之间的循序都要进行大规模调整。如果是面向对象的话,只用改动棋盘对象就行了,棋盘系统保存了黑白双方的棋谱,简单回溯就可以了,而显示和规则判断则不用顾及,同时整个对对象功能的调用顺序都没有变化,改动只是局部的。
19虚继承,菱形继承,普通继承的区别
虚继承时子类的虚函数不再是添加到父类部分的虚表中,而在普通的继承中确实直接添加到父类的虚表中,这就意味着如果虚继承中子类父类都有各自的虚函数,在子类里面就会多出一个虚表指针,而普通的继承却不会这样。
普通继承中父类在前,子类在后;虚拟继承中先是子类部分的内存,接着再是父类的内存。

在这里插入图片描述

20 Violate关键字
volatile 关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改,比如:操作系统,硬件或者其他线程等。

21 c++四种强制类型转换
在这里插入图片描述
在这里插入图片描述

22 c++内存泄露
内存泄漏一般是指堆内存的泄漏,也就是程序在运行过程中动态申请的内存空间不再使用后没有及时释放,导致那块内存不能被再次使用。
更广义的内存泄漏即资源泄露,还包括未对系统资源的及时释放,比如句柄、socket等没有使用相应的函数释放掉,导致系统资源的浪费。
23 sprintf、strcpy、strncpy及 memcpy 函数,请问这些函数功能有什么区别?
在这里插入图片描述

24 虚函数是怎么实现的
简单的说,如果一个类中有虚函数,那么编译器会给该类创建一个虚函数表(vtable),并添加一个指针vptr指向vtable。
虚函数是通过一张虚函数表实现的,有多少个虚函数,就有多少个指针
25 什么函数不能是虚函数,为什么?
不能被继承的函数和不能被重写的函数。
1)普通函数
普通函数不属于成员函数,是不能被继承的。普通函数只能被重载,不能被重写,因此声明为虚函数没有意义。因为编译器会在编译时绑定函数。
而多态体现在运行时绑定。通常通过基类指针指向子类对象实现多态。
2)友元函数
友元函数不属于类的成员函数,不能被继承。对于没有继承特性的函数没有虚函数的说法。
3)构造函数
首先说下什么是构造函数,构造函数是用来初始化对象的。假如子类可以继承基类构造函数,那么子类对象的构造将使用基类的构造函数,而基类构造函数并不知道子类的有什么成员,显然是不符合语义的。从另外一个角度来讲,多态是通过基类指针指向子类对象来实现多态的,在对象构造之前并没有对象产生,因此无法使用多态特性,这是矛盾的。因此构造函数不允许继承。
4)内联成员函数
我们需要知道内联函数就是为了在代码中直接展开,减少函数调用花费的代价。也就是说内联函数是在编译时展开的。而虚函数是为了实现多态,是在运行时绑定的。因此显然内联函数和多态的特性相违背。
5)静态成员函数
首先静态成员函数理论是可继承的。但是静态成员函数是编译时确定的,无法动态绑定,不支持多态,因此不能被重写,也就不能被声明为虚函数。
26 数据读写
ifstream in1, in2;
in1.open(“E:\shuju\ans1.txt”);
in1.close();
ofstream input(“E:\Ctime.txt”);//存入计时
input.close();
27 阻塞,非阻塞,同步异步
在这里插入图片描述
在这里插入图片描述
28 阻塞流,非阻塞流
阻塞流:当在对流进行读写操作时,如果没有信息或者暂时不可读写,那么程序就进入等待状态,直到可读写为止。
非阻塞流:如果没有信息或者暂时不可读写,那么读写函数就马上返回,不会等待。
29 指针函数和函数指针
在这里插入图片描述
在这里插入图片描述

30内联函数和普通函数的区别
在c/c++中,为了解决一些频繁调用的小函数大量消耗栈空间(栈内存)的问题,特别的引入了inline修饰符,表示为内联函数。
在这里插入图片描述

31,c++内存管理
在C++中,虚拟内存分为代码段、数据段、BSS段、堆区、文件映射区以及栈区六部分。
代码段:包括只读存储区和文本区,其中只读存储区存储字符串常量,文本区存储程序的机器代码。
数据段:存储程序中已初始化的全局变量和静态变量
bss 段:存储未初始化的全局变量和静态变量(局部+全局),以及所有被初始化为0的全局变量和静态变量。
堆区:调用new/malloc函数时在堆区动态分配内存,同时需要调用delete/free来手动释放申请的内存。
映射区:存储动态链接库以及调用mmap函数进行的文件映射
栈:使用栈空间存储函数的返回地址、参数、局部变量、返回值
32 ,指针和引用的区别
本质上的区别是,指针是一个新的变量,只是这个变量存储的是另一个变量的地址,我们通过访问这个地址来修改变量。
而引用只是一个别名,还是变量本身。对引用进行的任何操作就是对变量本身进行操作,因此以达到修改变量的目的。
在这里插入图片描述

33,为什么基类要有虚函数基类,为什么需要虚析构函数?
防止内存泄漏。想去借助父类指针去销毁子类对象的时候,不能去销毁子类对象。假如没有虚析构函数,释放一个由基类指针指向的派生类对象时,不会触发动态绑定,则只会调用基类的析构函数,不会调用派生类的。派生类中申请的空间则得不到释放导致内存泄漏。
34,vector和list的区别
在这里插入图片描述

35,C和C++中struct的区别是什么?
C语言中的struct与C++中的struct的区别表现在以下3个方面:
(1) C语言的struct不能有函数成员,而C++的struct可以有。
(2) C语言的struct中数据成员没有private、public和protected访问权限的设定,而C++ 的struct的成员有访问权限设定。
(3) C语言的struct是没有继承关系的,而C++的struct却有丰富的继承关系。
C语言中的struct是用户自定义数据类型,它是没有权限设置的,它只能是一些变量的集合体,虽然可以封装数据却不可以隐藏数据,而且成员不可以是函数。为 了和C语言兼容,C++中就引入了 struct关键字。C++语言中的struct是抽象数据类型 (ADT),它支持成员函数的定义,同时它增加了访问权限,它的成员函数默认访问权限为 public。
36 C语言中struct和union的区别是什么?
struct (结构体)与union (联合体)是C语言中两种不同的数据结构,两者都是常见的复合结构,其区别主要表现在以下两个方面:
(1)结构体与联合体虽然都是由多个不同的数据类型成员组成的,但不同之处在于联合体中所有成员共用一块地址空间,即联合体只存放了一个被选中的成员,而结构体中所有成员占用空间是累加的,其所有成员都存在,不同成员会存放在不同的地址。所以结构体在计算一个结构型变量的总长度时,其内存空间大小等于所有成员长度之和(需要考虑字节对齐),而在联合体中, 所有成员不能同时占用内存空间,它们不能同时存在,所以一个联合型变量的长度等于其最长的成员的长度。
(2)对于联合体的不同成员赋值,将会对它的其他成员重写,原来成员的值就不存在了, 而对结构体的不同成员赋值是互不影响的。
37,线程安全问题。
线程安全是应用于多线程代码的一种计算机编程概念,它确保多个线程能够按照程序的设计正确的访问共享数据结构。
或者再贴近编程语言的角度一点来讲,线程安全指的是同时最少有两个及以上的线程操作共享的数据区域,并且至少有一个是写操作
在这里插入图片描述

三,数据结构:
1,map和unordered_map区别
map基于红黑树,unordered_map基于哈希表
map不允许重复元素,有序,基于红黑树,删除效率高,头文件为#include < map >
unordered_map不允许重复元素,无序,基于哈希表。查找效率高,头文件#include < unordered_map >
https://blog.csdn.net/qq_21997625/article/details/84672775
哈希表较红黑树的空间占用率较高,但是执行效率要高于红黑树
在这里插入图片描述

2, 平衡二叉树,排序二叉树,B树
在这里插入图片描述

二叉树是每个结点最多有两个子树的树结构。通常子树被称作“左子树”和“右子树”。一棵深度为k,且有2k-1个节点的二叉树,称为满二叉树。除最后一层外,若其余层都是满的,并且最后一层或者是满的,或者是在右边缺少连续若干节点,则此二叉树为完全二叉树。具有n个节点的完全二叉树的深度为log2(n+1)。深度为k的完全二叉树,至少有2(k-1)个节点,至多有2^k-1个节点。

在这里插入图片描述

3 哈弗曼树
给定N个权值作为N个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。 应用:哈弗曼编码

4 DFS和BFS

在这里插入图片描述

四,操作系统
GPU相对于CPU的优缺点
CPU (Central Processing Unit) 即中央处理器,是机器的“大脑”,CPU需要很强的通用性来处理各种不同的数据类型,有复杂的逻辑控制单元。CPU的架构中需要大量的空间去放置存储单元(橙色部分)和控制单元(黄色部分),相比之下计算单元(绿色部分)只占据了很小的一部分,所以它在大规模并行计算能力上极受限制,而更擅长于逻辑控制。

GPU全称为Graphics Processing Unit,中文为图形处理器,GPU面对的则是类型高度统一的、相互无依赖的大规模数据。它的构成相对简单,有数量众多的计算单元和超长的流水线,特别适合处理大量的类型统一的数据。、
GPU的工作大部分都计算量大,但没什么技术含量,而且要重复很多很多次。GPU就是用很多简单的计算单元去完成大量的计算任务,纯粹的人海战术。这种策略基于一个前提,就是并行计算的线程之间没有什么依赖性,是互相独立的。
CPU更适合处理逻辑控制密集的计算任务,而GPU适合处理数据密集的计算任务。
例子:几个博士生(CPU)和几百个小学生(GPU)比赛算数计算。
目前比较流行CPU+GPU的协同计算模型,在这个模型中,CPU与CPU协同工作,各司其职。CPU负责进行逻辑性强的事物处理和串行计算,GPU则专注于执行高度线程化的并行处理任务。
(1)GPU 的计算能力远超 CPU
http://free.chinabaogao.com/it/201710/10162a4U2017.html
GPU有超高的浮点运算能力(G上万,C几百)和内存带宽(G500以上,C几十)
(2)GPU 还有一大优势是可以并行处理多个任务,其大规模并行计算能力用于人工智能神经网络之间的连接非常适合。如果一个核心的计算算作一个线程,那么 GPU 中多个线程同时进行数据的处理。尽管每个线程/Core 的计算性能、效率与 CPU 中的 Core 相比低了不少,但是当所有线程都并行计算,那么累加之后它的计算能力又远远高于 CPU。
(3)GPU 的共享内存结构可提高线程间通信速度。在 NVIDIA 披露的 GPU 性能参数中,每个流处理器集群末端设有共享内存。相比于 CPU 每次操作数据都要返回内存再进行调用, GPU 线程之间的数据通讯不需要访问全局内存,而在共享内存中就可以直接访问。这种设置的带来的好处就是线程间通讯速度的提高。
(4)GPU执行每个数值计算的速度并没有比CPU快,从目前主流CPU和GPU的主频就可以看出了,CPU的主频都超过了2GHz,甚至3GHz,而GPU的主频最高还不到2GHz,主流的也就1GHz。所以GPU在执行少量线程的数值计算时速度并不能超过CPU。
1,怎么让一个线程跑更长的时间。 -------加锁
在这里插入图片描述

2,死锁,死锁产生的条件,多线程如何避免死锁
死锁是指两个或两个以上进程在执行过程中,因争夺资源而造成的下相互等待的现象。死锁发生的四个必要条件如下:(例子:图书馆借书)
互斥条件:进程对所分配到的资源不允许其他进程访问,若其他进程访问该资源,只能等待,直至占有该资源的进程使用完成后释放该资源;
请求和保持条件:进程获得一定的资源后,又对其他资源发出请求,但是该资源可能被其他进程占有,此时请求阻塞,但该进程不会释放自己已经占有的资源
不可剥夺条件:进程已获得的资源,在未完成使用之前,不可被剥夺,只能在使用后自己释放
环路等待条件:进程发生死锁后,必然存在一个进程-资源之间的环形链
解决死锁的方法即破坏上述四个条件之一,主要方法如下:
资源一次性分配,从而剥夺请求和保持条件
可剥夺资源:即当进程新的资源未得到满足时,释放已占有的资源,从而破坏不可剥夺的条件
资源有序分配法:系统给每类资源赋予一个序号,每个进程按编号递增的请求资源,释放则相反,从而破坏环路等待的条件

避免死锁的一般准则:
线程死锁只发生在有锁的情况下。有时你创建了两个线程,这两个线程分别join另一个线程,这样任何join都无法返回,每个线程都在等待另一个线程结束,这就是两个孩子为了玩具打架一样。这种的问题可以发生在任何“一个线程正在等另一个线程做事,而另一个线程有时候也会等第一个线程做什么事”的时候。避免线程死锁归结为一个重要概念就是:A线程不要等待B线程,如果B线程有可能等待A线程。
1.避免嵌套锁定
这一条是最简单的,你已经锁定了一个mutex的时候,你最好不要再次锁定。如果你遵守了这条规则,因为一个线程只有一个锁的情况下不会造成死锁。但是也有其它原因会造成死锁(比如一个线程在等待另一个线程),如果你要锁定多个,你就用std::lock。
2.在已经持有锁的时候不要调用用户自义的代码
因为用户自定义的代码是无法预知的,谁知道他的代码里会不会也想要锁定这个lock。有时候无法避免不调用用户定义代码,这种情况下,你需要注意。
3.按固定顺序锁定
如果你要锁定两个以上的mutex而你又不能用std::lock。那么最好的建议就按固定顺序去锁定。
4.用层锁来防止死锁
hierarchical_mutex规则思想是:将mutex分层,规定加锁顺序是由高层到底层才能进行,底层到高层报出运行时错误,这样就可以利用编程的方法检测死锁。书中实现了hierarchical_mutex类作为可分层的mutex,

一群人在餐桌上,拿筷子,方向统一,

五,计算机网络:
1,TCP和udp的区别,TCP握手过程中 如何保持连接
https://blog.csdn.net/lin962792501/article/details/86589026
在这里插入图片描述

TCP是面向连接的,可靠的字节流服务,UDP是面向无连接的,不可靠的,面向报文的。
在这里插入图片描述

6个标志位:
*SYN:同步标志
   同步序列编号(Synchronize Sequence Numbers)栏有效。该标志仅在三次握手建立TCP连接时有效。它提示TCP连接的服务端检查序列编号,该序列编号为TCP连接初始端(一般是客户端)的初始序列编号。在这里,可以把TCP序列编号看作是一个范围从0到4,294,967,295的32位计数器。通过TCP连接交换的数据中每一个字节都经过序列编号。在TCP报头中的序列编号栏包括了TCP分段中第一个字节的序列编号。
  *ACK:确认标志
   确认编号(Acknowledgement Number)栏有效。大多数情况下该标志位是置位的。TCP报头内的确认编号栏内包含的确认编号(w+1,Figure-1)为下一个预期的序列编号,同时提示远端系统已经成功接收所有数据。
  *RST:复位标志
   复位标志有效。用于复位相应的TCP连接。
  *URG:紧急标志
   紧急(The urgent pointer) 标志有效。紧急标志置位,
  *PSH:推标志
   该标志置位时,接收端不将该数据进行队列处理,而是尽可能快将数据转由应用处理。在处理 telnet 或 rlogin 等交互模式的连接时,该标志总是置位的。
  *FIN:结束标志
   带有该标志置位的数据包用来结束一个TCP回话,但对应端口仍处于开放状态,准备接收后续数据
SYN(synchronous建立联机) ACK(acknowledgement 确认) PSH(push传送) FIN(finish结束) RST(reset重置) URG(urgent紧急)Sequence number(顺序号码) Acknowledge number(确认号码)
在这里插入图片描述

为什么要有三次握手?
这是因为,首先建立连接,必须要有两次握手吧,1客户端主动一次,告知服务端,我想和你建立连接,然后看服务端是否同意。2然后如果服务端同意的话,得给一个回复,然后开始等待客户端的数据包,这就是两次握手。3如果这个时候就建立连接,客户端开始给服务端发送数据包,在正常情况下没啥问题。
但是由于网络并不是100%任何时候都稳定,一旦因为某些原因导致服务端发送给客户端的确认报文丢失,那这个时候客户端收不到确认数据包,会误以为服务端不同意连接,不会给服务端发送数据包,但是这时候服务端已经在等待了。这样的差错会导致服务端一直处于等待状态,浪费资源。
而三次握手的话,客户端在确认服务端同意之后再发一次数据包给服务端,告知服务端,不管怎么样我都会给你发送数据包的。因为TCP协议中,建立连接之后,每一次发送数据包,客户端都会等待服务端的确认信息,如果收不到某一个数据包的确认信息,客户端就会重发这个数据包。这样就不会发生差错了。
现在把三次握手改成仅需要两次握手,死锁是可能发生的。作为例子,考虑计算机S和C之间的通信,假定C给S发送一个连接请求分组,S收到了这个分组,并发送了确认应答分组。按照两次握手的协定,S认为连接已经成功地建立了,可以开始发送数据分组。可是,C在S的应答分组在传输中被丢失的情况下,将不知道S是否已准备好,不知道S建立什么样的序列号,C甚至怀疑S是否收到自己的连接请求分组。在这种情况下,C认为连接还未建立成功,将忽略S发来的任何数据分组,只等待连接确认应答分组。而S在发出的分组超时后,重复发送同样的分组。这样就形成了死锁。

TCP的连接释放
在这里插入图片描述

以上就是TCP连接释放的4次握手,也可以叫做四次挥手,也可以看作是两个二次握手(挥手)。
1.数据传输结束后,通信的双方都可以释放连接。此时,客户机和服务器都处于ESTABLISHED(已建立连接)状态。
  2.假设客户机请求完资源了,想要释放连接。首先,客户机的应用进程先向服务器发出连接释放报文段,该报文段中将首部的终止控制位FIN置为1(只有当FIN置为1时,才能表明客户机想要和服务器断开连接),并且序号为u(注意:此时的u不是随机产生的,而是之前客户机传送的数据的最后一个字节的序号加1)。此时客户机进入到FIN-WAIT-1(终止等待1)状态,等待服务器的确认。
  3.服务器收到连接释放报文后发出确认,在发送报文中将首部中的ACK置为1(ACK置为1,表面服务器同意与客户机释放连接),并且产生序号v(注意:此时的v不是随机产生的,而是之前服务器传送的数据的最后一个字节的序号加1),并且发出确认号为u+1(确认号表明服务器渴望收到的下一个报文段的第一个数据字节的序号,因为之前发送了u,所以下一个序号为u+1)。此时服务器就进入CLOSE-WAIT(关闭等待)状态,客户机进入FIN-WAIT-2状态。
此时,从客户机到服务器这个方向的连接就被释放了,也就是说,客户机已经没有数据要向服务器发送了,但是如果服务器向客户机发送数据,客户机仍要接收数据。也就是说:从客户机到服务器的连接已经被释放了,但是从服务器到客户机的连接还没被释放。此时,TCP连接处于半关闭状态。
4.如果服务器向客户机也没有要发送的数据的话,那么服务器的应用进程就可以向客户机发出连接释放报文段(注意此时还是服务器向客户机发送数据),该报文段中将首部的终止控制位FIN置为1(只有当FIN置为1时,才能表明客户机想要和服务器断开连接),ACK也置为1,并且序号为w(重点注意,此时的w不一定等于v+1。如果在客户机释放了连接之后,服务器向客户机仍旧发送了一部分数据,那么此时w不等于v+1,但是如果期间没有再发送数据,那么w就等于v+1。总而言之,这个w等于服务器上一次发送的数据的最后一个字节加1),并且发送确认号为u+1(确认号表明服务器渴望收到的下一个报文段的第一个数据字节的序号,因为之前发送了u,所以下一个序号为u+1)。此时服务器就进入了LAST-ACK(最后确认)状态。
5.客户机收到服务器的连接释放报文后,必须对此报文进行确认。在该报文段中将ACK置为1,确认号为w+1(确认号表明服务器渴望收到的下一个报文段的第一个数据字节的序号,因为之前发送了w,所以下一个序号为w+1),产生序号为u+1(因为上一个发送的数据的序号为u)。此时服务器进入到TIME-WAIT(等待时间)状态。但是,此时TCP连接还没有被释放掉。必须经过2MSL后服务器才能进入到CLOSED状态。(注:MSL叫做最长报文段寿命,RFC建议为两分钟,也就是说,要经过四分钟才能进入到CLOSED状态)。

在这里插入图片描述
在这里插入图片描述
2,计算机的5层协议
在这里插入图片描述

3请回答一下HTTP和HTTPS的区别,以及HTTPS有什么缺点?
HTTP协议和HTTPS协议区别如下:
1)HTTP协议是以明文的方式在网络中传输数据,而HTTPS协议传输的数据则是经过TLS加密后的,HTTPS具有更高的安全性
2)HTTPS在TCP三次握手阶段之后,还需要进行SSL 的handshake,协商加密使用的对称加密密钥
3)HTTPS协议需要服务端申请证书,浏览器端安装对应的根证书
4)HTTP协议端口是80,HTTPS协议端口是443
HTTPS优点:
HTTPS传输数据过程中使用密钥进行加密,所以安全性更高
HTTPS协议可以认证用户和服务器,确保数据发送到正确的用户和服务器
HTTPS缺点:
HTTPS握手阶段延时较高:由于在进行HTTP会话之前还需要进行SSL握手,因此HTTPS协议握手阶段延时增加
HTTPS部署成本高:一方面HTTPS协议需要使用证书来验证自身的安全性,所以需要购买CA证书;另一方面由于采用HTTPS协议需要进行加解密的计算,占用CPU资源较多,需要的服务器配置或数目高

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值