2022年春季招聘问题整理
- 继承的方式有哪些?具体来说
三种继承方式:也是 public,protected,private
当public父类继承时public类型的成员将继承到子类public中去,而父类protected成员也将继承到子类protected中去
当protected继承时,无论是在父类中,public的还是protected所有成员都将继承到子类protected中去
当private父类继承时 public 和 protected 所有成员都将继承到子类private中去
数组:数组是用来存储多相同类型的数据。 指针:指针相当于一个变量,但它不同于不同的变量,它存储在内存中其他变量的地址。 区别
- 赋值:同类型指针变量可以相互赋值,但数组不能,只能赋值或复制一个元素
- 存储方式:数组:数组连续存储在内存中,开辟连续的内存空间。指针:指针非常灵活,可以指向任何类型的数据。
- 求sizeof:数组所占存储空间的内存:sizeof(数组名),数组大小:sizeof(数组名)/sizeof(数据类型)。在32个平台下,无论指针的类型是什么,sizeof(指针名)均为4,在64个平台下,无论指针类型如何,sizeof(指针名)都是8。
- 不同的初始化方法。 1、C面向过程的语言,C 是面向对象的语言 2、C和C 动态管理内存的方法不同,C是使用malloc/free函数,而C 除此之外new/delete关键字 3、C C没有引用; 有四个过程:编译和链接: (1)预处理 (2)编译 (3)汇编 (4)链接 第一次握手:建立连接时,客户发送syn包(syn=j)并进入服务器SYN_SENT状态,等待服务器确认;SYN:同步序列号(Synchronize Sequence Numbers)。 第二次握手:服务器收到syn必须确认客户的包SYN(ack=j 1),同时自己也发送一个SYN包(syn=k),即SYN ACK此时服务器进入包SYN_RECV状态; 第三次握手:客户端收到服务器SYN ACK将确认包发送到服务器ACK(ack=k 1)发送此包后,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。 1、TCP面向连接(如需拨号建立连接);UDP是无连接 也就是说,在发送数据之前不需要建立连接 2、TCP提供可靠的服务。换句话说,通过TCP连接传输数据,无错误,无丢失,无重复,并按顺序到达;UDP尽最大努力交付,即不保证 证可靠交付 3、TCP面对字节流,其实是TCP将数据视为一系列无结构的字节流;UDP面向报文,应用层交给UDP报文多长,UDP就照样发送,即一次发送一个报文。UDP没有拥塞控制,因此网络拥塞不会降低源主机的发送速率(对实时应用非常有用,如IP电话、实时视频会议等) 4、每一条TCP连接只能点到点;UDP支持一对一、一对多、多对一、多对多的交互通信 5、TCP第一个开销20字节;UDP第一个开销很小,只有8个字节 6、TCP逻辑通信信道是全双工的可靠信道,UDP不可靠的信道 ①内部成员变量及成员函数的默认访问权限 使用struct默认情况下,其成员的访问权public的,而class默认成员是private的。 ②继承关系 struct默认继承是public继承,而class默认继承是private继承 ③使用模板 class可用作模板,而struct不能 结论:当你认为你想做的更像是一个数据结构集合时,使用它struct。如果你想做的更像是一个对象,那就用吧class。 联系:它们都是定义常量的方法。 区别: define没有类型定义常量;const常量的定义是有类型的。 define在预处理阶段替换定义常量,const在编译阶段确定其值。 const函数可以定义define不可以。 define类型安全检查不会进行,但是const类型安全检查,安全性更高。 static它意味着静态,可用于修改变量、函数和类成员。 变量:被static修改后的变量是静态变量,它将永远存在于程序运行中,并将其放置在静态存储区域。本文件中存在局部静态变量的功能域,本文件中存在全局静态变量的功能域。 函数:被static修改函数是静态函数,静态函数只能在本文件中使用,不能被其他文件调用,也不能与其他文件中的同名函数冲突。 类:而在类中,被子static修改后的成员变量为静态成员,该静态成员由多个对象共享。static修改后的成员函数也属于静态成员,不属于某个对象。访问这个静态函数不需要引用对象名,而是引用类名。 线性探索。当该元素的哈希值对应的桶不能存储元素时,个接一个地搜索,直到找到一个空桶,搜索也是如此。当哈希值对应位置的元素与要找到的元素不同时,一个接一个地搜索,直到找到合适的元素或空桶。 第二次探索。当该元素的哈希值对应的桶无法存储元素时,寻找12、22、32、42…i^2个位置。 双散列函数法。当第一个散列函数发生冲突时,使用第二个散列函数作为步长。 STL中的vector它是一个装有动态数组的顺序容器。但与动态数组不同,vector可以根据需要自动扩大容器的大小。具体策略是每次容量不够用时重新申请一块大小为原来容量两倍的内存,将原容器的元素拷贝至新容器,并释放原空间,返回新空间的指针。 当原始空间不足以存储新值时,每次调用push_back该方法将重新分配新的空间,以满足新数据的添加。如果这种操作在程序中经常进行,它仍然消耗性能。 如需频繁插入,最好先指定vector因为vector当容器尺寸不够时,将重新申请一个尺寸为原容器两倍的空间,并将原容器元素复制到新容器中,释放原始空间。这个过程非常耗时和内存。频繁调用push_back()程序会花很多时间vector扩容会变慢。这种情况可以考虑使用list。 重载(overload)指函数名相同,参数列表不同的函数实现方法。 重写(overwide)指函数名相同,参数列表相同,只有方法体不同。 在C 其中,内存分为堆叠、堆栈、全局/静态存储区、常量存储区和代码区五个区域。 栈,在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。 堆,就是那些原因new分配的内存块由我们的应用程序控制,通常是一个new对应一个delete。如果程序员没有释放,操作系统将在程序结束后自动回收。 整体/静态存储区域在编译程序时已经分配,存在于程序的整个运行过程中。它主要存储静态数据(局部)static变量,全局static变量)、全局变量和常量。 常量存储区是一个特殊的存储区,存储在常量字符串中,不允许修改。 代码区,存储程序的二进制代码。 面向对象的三个特点是:包装、继承和多态。 包装隐藏了类的实现细节和成员数据,实现了代码模块化,如类private和public; 继承使子类能够重用父类的成员和方法,实现代码重用; 多态是一个接口,多个实现,通过父类调用子类成员,实现接口重用,如父类指针指向子类对象。 C 多态包括编译时多态和运行时多态,编译时多态反映在函数重载和模板上,运行时多态反映在虚拟函数上。 虚拟函数:在基类函数之前添加virtual关键字,在衍生类中重写函数,并根据对象的实际类型调用相应的函数。如果对象类型为衍生类,则调用衍生函数;如果对象类型为基本类,则调用基本类的函数. C 构造函数主要有三种类型:默认构造函数、重载构造函数和复制构造函数 默认构造函数是编译器默认提供的构造函数。 重载结构函数也称为一般结构函数。一个类可以有多个重载结构函数,但参数类型或数量不同。重载结构函数中的自定义初始化模式。 复制构造函数发生象复制的时候调用的。 因为结构体的成员可以有不同的数据类型,所占的大小也不一样。同时,由于CPU读取数据是按块读取的,内存对齐可以使得CPU一次就可以将所需的数据读进来。 “”以空间换时间“” 动态分配内存所开辟的空间,在使用完毕后未手动释放,导致一直占据该内存,即为内存泄漏。 1)类的构造函数和析构函数中new和delete没有配套 2)在释放对象数组时没有使用delete[],使用了delete 避免方法: malloc/free要配套;将基类的析构函数设为虚函数; 预处理,编译,汇编,链接 预处理: 对预处理命令进行替换等预处理操作 编译:代码优化和生成汇编代码 汇编:将汇编代码转化为机器语言 链接:将目标文件彼此链接起来 set,map的插入复杂度就是红黑树的插入复杂度,是log(N)。 unordered_set,unordered_map的插入复杂度是常数,最坏是O(N). vector的插入复杂度是O(N),最坏的情况下(从头插入)就要对所有其他元素进行移动,或者扩容重新拷贝 声明是告诉编译器变量的类型和名字,不会为变量分配空间 定义就是对这个变量和函数进行内存分配和初始化。需要分配空间,同一个变量可以被声明多次,但是只能被定义一次 #define是预处理命令,在预处理是执行简单的替换,不做正确性的检查 typedef是在编译时处理的,它是在自己的作用域内给已经存在的类型一个别名
计网相关
(3) 对路由协议的了解与介绍。内部网关协议IGP包括RIP,OSPF,和外部网关协议EGP和BGP. 因为UDP是无连接的协议,所以在传输层上无法保证可靠传输,要想实现可靠传输,只能从应用层实现。需要实现seq/ack机制,重传机制和窗口确认机制。 就要接收方收到UDP之后回复个确认包,发送方有个机制,收不到确认包就要重新发送,每个包有递增的序号,接收方发现中间丢了包就要发重传请求,当网络太差时候频繁丢包,防止越丢包越重传的恶性循环,要有个发送窗口的限制,发送窗口的大小根据网络传输情况调整,调整算法要有一定自适应性。 TCP族的协议有HTTP,HTTPS,SMTP,TelNet,FTP等,UDP族的协议有DNS,DHCP等等。(UDP和D相关) TCP的头部大致包括:源端口,目的端口,序号,确认号,偏移位,标志位,校验和等等 UDP的头部则包括:源端口,目的端口,长度,校验和。 IP数据包的头部包括:源IP地址,目的IP地址,协议,校验和,总长度等等 第一次握手:首先client给server发送连接请求报文,在这个报文中,包含了SYN=1,client_seq=任意值i,发送之后处于SYN-SENT状态,这是第一次握手 第二次握手:server端接收到了这个请求,并分配资源,同时给client返回一个ACK报文,这个报文中呢包含了这些字段,标志位SYN和ACK都为1,而小ack为i+1,此时位于SYN-RCVD状态,这是第二次握手 第三次握手:client收到server发来的ACK信息后呢,他会看到server发过来的小ack是i+1,这时他知道了server收到了消息,也给server回一个ACK报文,报文中同样包含了ACK=1这样的消息,同时呢,还包括了client_ack=k+1这样的字段,这样呢三次握手之后,连接就建立了,client进入established(已建立连接)状态 : TCP断开连接通常是由一方主动,一方被动的,这里我们假设client主动,server被动 第一次挥手:当client没有数据要发送给server了,他会给server发送一个FIN报文,告诉server:“我已经没有数据要发给你了,但是你要是还想给我发数据的话,你就接着发,但是你得告诉我你收到我的关闭信息了”,这是第一次挥手,挥手之后client进入FIN_WAIT_1的第一阶段 第二次挥手:当server收到client发来的FIN报文后,告诉client:“我收到你的FIN消息了,但是你等我发完的”此时给client返回一个ACK信息,并且呢ack=seq+1,这是第二次挥手,挥手之后呢server进入CLOSE_WAIT阶段,而client收到之后处于FIN_WAIT_2第二阶段 第三次挥手:当server发完所有数据时,他会给client发送一个FIN报文,告诉client说“我传完数据了,现在要关闭连接了”,然后呢server变成LAST_ACK状态,等着client最后的ACK信息,这是第三次挥手 第四次挥手:当client收到这个FIN报文时,他会对这个消息进行确认,即给server发ACK信息,但是它不相信网络,怕server收不到信息,它会进入TIME_WAIT状态,万一server没收到ACK消息它可以可以重传,而当server收到这个ACK信息后,就正式关闭了tcp连接,处于CLOSED状态,而client等待了2MSL这样长时间后还没等到消息,它知道server已经关闭连接了,于是乎他自己也断开了,这是第四次挥手,这样tcp连接就断开了 如果使用两次握手的话,三次握手中的最后一次缺失,服务器不能确认客户端的接收能力。 举两个例子,第一种是黑客会伪造大量SYN请求发送给服务器,服务器立即确认并建立连接,分配资源,但是这一系列连接并不是真实存在的,这大大浪费了服务器的资源并且阻塞了正常用户的连接,这种也叫SYN洪泛攻击。 第二种是服务器返回给客户端的ACK数据包可能会在传输的过程中丢失,而客户端没有收到该ACK数据包而拒绝接收服务器接下来发送的数据,于是服务器一直在发送,客户端一直在拒绝,形成死锁。 (校序重流拥)
校验和 发送的数据包的二进制相加然后取反,目的是检测数据在传输过程中的任何变化。如果收到段的检验和有差错,TCP将丢弃这个报文段和不确认收到此报文段。
确认应答+序列号 TCP给发送的每一个包进行编号,接收方对数据包进行排序,把有序数据传送给应用层。
超时重传 当TCP发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能及时收到一个确认,将重发这个报文段。
流量控制 TCP连接的每一方都有固定大小的缓冲空间,TCP的接收端只允许发送端发送接收端缓冲区能接纳的数据。当接收方来不及处理发送方的数据,能提示发送方降低发送的速率,防止包丢失。TCP使用的流量控制协议是可变大小的滑动窗口协议。 接收方有即时窗口(滑动窗口),随ACK报文发送
拥塞控制 当网络拥塞时,减少数据的发送。 发送方有拥塞窗口,发送数据前比对接收方发过来的即使窗口,取小
慢启动、拥塞避免、快速重传、快速恢复 拥塞控制是防止过多的数据注入到网络中,导致网络发生拥塞;而流量控制是防止发送方一下子发送过多的数据到接收方,导致接收方缓存放不下。两种算法都是对发送方的行为进行控制的。 HTTP的请求方法包括GET,POST,PUT,DELETE四种基本方法。(四种方法中只有POST不是操作幂等性的) 第一次的序号是随机序号,但也不是完全随机,它是使用一个ISN算法得到的。 seq = C + H (源IP地址,目的IP地址,源端口,目的端口)。其中,C是一个计时器,每隔一段时间值就会变大,H是消息摘要算法,输入是一个四元组(源IP地址,目的IP地址,源端口,目的端口)。 因为TCP为了减少额外开销,采取的是流式传输,所以接收端在一次接收的时候有可能一次接收多个包。而TCP粘包就是发送方的若干个数据包到达接收方的时候粘成了一个包。多个包首尾相接,无法区分。 导致TCP粘包的原因有三方面: 发送端等待缓冲区满才进行发送,造成粘包 接收方来不及接收缓冲区内的数据,造成粘包 由于TCP协议在发送较小的数据包的时候,会将几个包合成一个包后发送 避免粘包的措施: 通过编程,强制使TCP发生数据传送,不必等到缓冲区满 优化接收方接收数据的过程,使其来得及接收数据包,包括提高接收进程优先级等 设置固定长度的报文或者设置报文头部指示报文的长度。 DNS解析有两种方式:递归查询和迭代查询 递归查询 用户先向本地域名服务器查询,如果本地域名服务器的缓存没有IP地址映射记录,就向根域名服务器查询,根域名服务器就会向顶级域名服务器查询,顶级域名服务器向权限域名服务器查询,查到结果后依次返回。 迭代查询 用户向本地域名服务器查询,如果没有缓存,本地域名服务器会向根域名服务器查询,根域名服务器返回顶级域名服务器的地址,本地域名服务器再向顶级域名服务器查询,得到权限域名服务器的地址,本地域名服务器再向权限域名服务器查询得到结果 这都是由于B+树和B具有不同的存储结构所造成的区别,以一个m阶树为例。
关键字的数量不同;B+树中分支结点有m个关键字,其叶子结点也有m个,其关键字只是起到了一个索引的作用,但是B树虽然也有m个子结点,但是其只拥有m-1个关键字。 存储的位置不同;B+树中的数据都存储在叶子结点上,也就是其所有叶子结点的数据组合起来就是完整的数据,但是B树的数据存储在每一个结点中,并不仅仅存储在叶子结点上。 分支结点的构造不同;B+树的分支结点仅仅存储着关键字信息和儿子的指针(这里的指针指的是磁盘块的偏移量),也就是说内部结点仅仅包含着索引信息。 查询不同;B树在找到具体的数值以后,则结束,而B+树则需要通过索引找到叶子结点中的数据才结束,也就是说B+树的搜索过程中走了一条从根结点到叶子结点的路径。 B+树优点:由于B+树的数据都存储在叶子结点中,分支结点均为索引,方便扫库,只需要扫一遍叶子结点即可,但是B树因为其分支结点同样存储着数据,我们要找到具体的数据,需要进行一次中序遍历按序来扫,所以B+树更加适合在区间查询的情况,所以通常B+树用于数据库索引,而B树则常用于文件索引。 以MYSQL为例, 按照类型来分有乐观锁和悲观锁 根据粒度来分有行级锁,页级锁,表级锁(粒度一个比一个大) (仅BDB,Berkeley Database支持页级锁) 根据作用来分有共享锁(读锁)和排他锁(写锁)。
操作系统
区别 进程是对运行时程序的封装,是系统进行资源分配和调度的基本单元,而线程是进程的子任务,是CPU分配和调度的基本单元。 一个进程可以有多个线程,但是一个线程只能属于一个进程。 进程的创建需要系统分配内存和CPU,文件句柄等资源,销毁时也要进行相应的回收,所以进程的管理开销很大;但是线程的管理开销则很小。 进程之间不会相互影响;而一个线程崩溃会导致进程崩溃,从而影响同个进程里面的其他线程。 联系 进程与线程之间的关系:线程是存在进程的内部,一个进程中可以有多个线程,一个线程只能存在一个进程中。 先来先服务 (FCFS first come first serve):短作业优先 (SHF short job first):高响应比优先调度算法:时间片轮转:多级反馈队列(Multilevel Feedback Queue) (1) 互斥:一个资源每次只能被一个进程使用。 (2) 占有并请求:一个进程因请求资源而阻塞时,对已获得的资源保持不放。 (3) 不可剥夺:进程已获得的资源,在末使用完之前,不能强行剥夺。 (4) 循环等待:若干进程之间形成一种头尾相接的循环等待资源关系。 产生死锁的原因主要是: (1) 因为系统资源不足。 (2) 进程运行推进的顺序不合适。 (3) 资源分配不当等。 (9) 死锁的恢复 重新启动:是最简单、最常用的死锁消除方法,但代价很大,因为在此之前所有进程已经完成的计算工作都将付之东流,不仅包括死锁的全部进程,也包括未参与死锁的全部进程。 终止进程(process termination):终止参与死锁的进程并回收它们所占资源。 (1) 一次性全部终止;(2) 逐步终止(优先级,代价函数) 剥夺资源(resource preemption):剥夺死锁进程所占有的全部或者部分资源。 (1) 逐步剥夺:一次剥夺死锁进程所占有的一个或一组资源,如果死锁尚未解除再继续剥夺,直至死锁解除为止。 (2) 一次剥夺:一次性地剥夺死锁进程所占有的全部资源。 进程回退(rollback):让参与死锁的进程回退到以前没有发生死锁的某个点处,并由此点开始继续执行,希望进程交叉执行时不再发生死锁。但是系统开销很大: (1) 要实现“回退”,必须“记住”以前某一点处的现场,而现场随着进程推进而动态变化,需要花费大量时间和空间。 (2) 一个回退的进程应当“挽回”它在回退点之间所造成的影响,如修改某一文件,给其它进程发送消息等,这些在实现时是难以做到的 饥饿是由于资源分配策略不公引起的,当进程或线程无法访问它所需要的资源而不能继续执行时,就会发生饥饿现象。 线程之间通信: 使用全局变量;使用信号机制;使用事件 进程之间同步: 信号量;管程 临界区;信号量;事件;互斥量 该进程的地址空间;全局变量;堆空间
数据库
内连接是保证两个表中所有的行都要满足连接条件,而外连接则不然。
在外连接中,某些不满条件的列也会显示出来,也就是说,只限制其中一个表的行,而不限制另一个表的行。分左连接、右连接、全连接三种。
SQL语言包括哪些类型? 数据定义:Create Table,Alter Table,Drop/Truncate Table, Create/Drop Index
数据操纵:Select ,Insert,Update,Delete
数据控制:Grant,Revoke