都不是很高大上的东西,只是我复习准备面试字节的时候背的东西, 有些问题面试的时候没问。
答案都不是我原创的,是我准备复习的时候四处收集的,收集到了一个word 里面。 感觉考的概率挺大的,最起码能说出来怎么回事就行。
内推
字节跳动2021校园招聘研发提前批内部推荐全面启动!
在简历里面带上我的内推码,直达提前批现场。 不限制职位任何职位都可以投递。
我的内推码:FKPR3XJ
职位入口: https://job.toutiao.com/s/J8J8GuK
可以加qq: 1576554670 #帮忙查状态
在简历里面带上我的内推码,直达提前批现场。 不限制职位任何职位都可以投递。
我的内推码:FKPR3XJ
职位入口: https://job.toutiao.com/s/J8J8GuK
可以加qq: 1576554670 #帮忙查状态
静态多态与动态多态
静态多态:也称为编译期间的多态,编译器在编译期间完成的,编译器根据函数实参的类型 (可能会进行隐式类型转换),可推断出要调用那个函数,如果有对应的函数就调用该函数, 否则出现编译错误。
静态多态有两种实现方式:
- 函数重载:包括普通函数的重载和成员函数的重载
- 函数模板的使用
为什么 C 语言中没有重载呢?
编译器在编译期间创建的一个字符串,用来指明函数的定义或原型。C 和 C++程序的函数在 内部使用不同的名字修饰方式。
动态多态 动态多态(动态绑定):
即运行时的多态,在程序执行期间(非编译期)判断所引用对象的实际类 型,根据其实际类型调用相应的方法。
纯虚函数
纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己 的实现方法。在基类中实现纯虚函数的方法是在函数原型后加“=0” 。 包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。纯虚函数在派生 类中重新定义以后,派生类才能实例化出对象。 通过基类的引用或指针调用,调用基类还是派生类的虚函数,要根据运行时根据指针或引用 实际指向或引用的类型确定,调用非虚函数时,则无论基类指向的是何种类型,都调用基类 的函数 C++ 多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数.
请你回答一下静态变量什么时候初始化
参考回答:
静态变量存储在虚拟地址空间的数据段和 bss 段,C 语言中其在代码执行之前初始化,属于 编译期初始化。而 C++中由于引入对象,对象生成必须调用构造函数,因此 C++规定全局 或局部静态对象当且仅当对象首次用到时进行构造。
STL 中 map 与 hash_map 的比较
1. map : C++的 STL 中 map 是使用树来做查找算法; 时间复杂度:O(log2N)
2. hash_map : 使用 hash 表来排列配对,hash 表是使用关键字来计算表位置; 时间复杂度:O(1), 最 坏的时间复杂度:O(n) hash_map 比 map 查找速度快
解决哈希冲突的四种方法
1. 开放定址法: 所谓的开放定址法就是一旦发生了冲突,就去寻找下一个空的散列地址,只要散列表足 够大,空的散列地址总能找到,并将记录存入。
2. 链式地址法(HashMap 的哈希冲突解决方法) 对于相同的哈希值,使用链表进行连接。使用数组存储每一个链表表头。
3. 再哈希法: 再哈希法又叫双哈希法,有多个不同的 Hash 函数,当发生冲突时,使用第 二个,第三个,….,等哈希函数。 计算地址,直到无冲突。虽然不易发生聚集,但是增 加了计算时间。
4. 建立公共溢出区: 这种方法的基本思想是:将哈希表分为基本表和溢出表两部分,凡是和基本表发生冲突 的元素,一律填入溢出表
B 树 与 B+树 (听说挺喜欢问,但是面试没问我):
B+树中,有 n 棵子树的结点中含有 n 个关键词;B 树中,有 n 棵子树的结点中含有 n-1 个 关键词。B+树中,所有的叶结点中包含了全部关键词,即其它非叶结点中的关键词都包含在 叶结点中,而在 B 树中,叶子结点包含的关键词与其它结点包含的关键词是不重复的;
由于 B 树的结点代表一个辅存页或者是一个辅存块,从一个结点到另一个结点需要费时的 页切换。 B+树中,非叶结点仅起索引作用,而 B 树中,每个关键词对应一个记录的存储地址 。
B+树上有两个头指针,一个指向根,另一个指向最小关键词的叶子结点 B+叶子结点按关键词自小而大的顺序链接。
B+ 树进行两种查找运算:一种是从最小关键词开始进行顺序查找,另一种是从根结点开始 进行随机查找。
B+树还有一个最大的好处,方便扫库,B 树必须用中序遍历的方法按序扫库,而 B+树直接 从叶子结点挨个遍历,B+树支持 range-query 非常方便,而 B 树不支持。
Python的 Yield
简单地讲,yield 的作用就是把一个函数变成一个 generator,带有 yield 的函数不再 是一个普通函数,Python 解释器会将其视为一个 generator
网络
TCP(Transmission Control Protocol)传输控制协议
TCP 是面向连接的、可靠的流协议。TCP 为提供可靠性传输,实行“顺序控制”或“重发控制” 机制。此外还具备 “流量控制”、“拥塞控制”。 此外,TCP 作为一种面向有连接的协议,只有在确认通信对端存在才会发送数据,从而可 以防止通信流量的浪费。
UDP (User Datagram Protocol) 用户数据报协议
UDP 是不具有可靠性的数据报协议 不能保证信息一定会到达。因此,应用有时会根据自己的需要进行重发处理。 UDP 不提供 复杂的控制机制,利用 IP 提供面向无连接的通信服务
TCP 的三次握手与四次挥手理解及面试题
【问题 1】为什么连接的时候是三次握手,关闭的时候却是四次握手?
答:因为当 Server 端收到 Client 端的 SYN 连接请求报文后,可以直接发送 SYN+ACK 报文。 其中 ACK 报文是用来应答的,SYN 报文是用来同步的。但是关闭连接时,当 Server 端收到 FIN 报文时,很可能并不会立即关闭 SOCKET,所以只能先回复一个 ACK 报文,告诉 Client 端,"你发的 FIN 报文我收到了"。只有等到我 Server 端所有的报文都发送完了,我才能发送FIN 报文,因此不能一起发送。故需要四步握手。
【问题 2】为什么 TIME_WAIT 状态需要经过 2MSL(最大报文段生存时间)才能返回到 CLOSE 状态?
TIME-WAIT: 等待足够的时间以确保远程 TCP 接收到连接中断请求的确认 等待 2MSL 时间主要目的是怕最后一个 ACK 包对方没收到,那么对方在超时后将重发第三次握手的 FIN 包,主动关闭端接到重发的 FIN 包后可以再发一个 ACK 应答包
答:虽然按道理,四个报文都发送完毕,我们可以直接进入 CLOSE 状态了,但是我们必须 假象网络是不可靠的,有可以最后一个 ACK 丢失。所以 TIME_WAIT 状态就是用来重发可能 丢失的 ACK 报文。在 Client 发送出最后的 ACK 回复,但该 ACK 可能丢失。Server 如果没有 收到 ACK,将不断重复发送 FIN 片段。所以 Client 不能立即关闭,它必须确认 Server 接收 到了该 ACK。Client 会在发送出 ACK 之后进入到 TIME_WAIT 状态。Client 会设置一个计时 器,等待 2MSL 的时间。如果在该时间内再次收到 FIN,那么 Client 会重发 ACK 并再次等待 2MSL。所谓的 2MSL 是两倍的 MSL(Maximum Segment Lifetime)。MSL 指一个片段在网络中 最大的存活时间,2MSL 就是一个发送和一个回复所需的最大时间。如果直到 2MSL,Client 都没有再次收到 FIN,那么 Client 推断 ACK 已经被成功接收,则结束 TCP 连接。
【问题 3】为什么不能用两次握手进行连接?
答:3 次握手完成两个重要的功能,既要双方做好发送数据的准备工作(双方都知道彼此已准 备好),也要允许双方就初始序列号进行协商,这个序列号在握手过程中被发送和确认。 现在把三次握手改成仅需要两次握手,死锁是可能发生的。作为例子,考虑计算机 S 和 C 之间的通信,假定 C 给 S 发送一个连接请求分组,S 收到了这个分组,并发 送了确认应 答分组。按照两次握手的协定,S 认为连接已经成功地建立了,可以开始发送数据分组。可 是,C 在 S 的应答分组在传输中被丢失的情况下,将不知道 S 是否已准备好,不知道 S 建 立什么样的序列号,C 甚至怀疑 S 是否收到自己的连接请求分组。在这种情况下,C 认为连 接还未建立成功,将忽略 S 发来的任何数据分 组,只等待连接确认应答分组。而 S 在发出 的分组超时后,重复发送同样的分组。这样就形成了死锁。
【问题 4】如果已经建立了连接,但是客户端突然出现故障了怎么办?
TCP 还设有一个保活计时器,显然,客户端如果出现故障,服务器不能一直等下去,白白浪 费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为 2 小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔 75 秒钟发送一次。若一连发送 10 个探测报文仍然没反应,服务器就认为客户端出了故障, 接着就关闭连接。 IP 头中有一个 TTL 域,TTL 是 time to live 的缩写,中文可以译为“生存时间”,这个生存时间 是由源主机设置初始值但不是存的具体时间,而是存储了一个 IP 数据报可以经过的最大路 由数,每经过一个处理他的路由器此值就减 1,当此值为 0 则数据报将被丢弃,同时发送 ICMP 报文通知源主机。
TCP 之 流量控制(滑动窗口)和 拥塞控制(拥 塞控制的工作过程)
DNS 用的是 TCP 协议还是 UDP 协议
DNS 占用 53 号端口,同时使用 TCP 和 UDP 协议。那么 DNS 在什么情况下使用这两种 协议? DNS 在区域传输的时候使用 TCP 协议,其他时候使用 UDP 协议。 DNS 区域传输的时候使用 TCP 协议: 1.辅域名服务器会定时(一般 3 小时)向主域名服务器进行查询以便了解数据是否有变 动。如有变动,会执行一次区域传送,进行数据同步。区域传送使用 TCP 而不是 UDP, 因为数据同步传送的数据量比一个请求应答的数据量要多得多。 2.TCP 是一种可靠连接,保证了数据的准确性。 域名解析时使用 UDP 协议: 客户端向 DNS 服务器查询域名,一般返回的内容都不超过 512 字节,用 UDP 传输即 可。不用经过三次握手,这样 DNS 服务器负载更低,响应更快。理论上说,客户端也可 以指定向 DNS 服务器查询时用 TCP,但事实上,很多 DNS 服务器进行配置的时候,仅 支持 UDP 查询包。
ARP 协议是“Address Resolution Protocol”(地址解析协议)的缩写。其作用是在以太网 环境中,数据的传输所依懒的是 MAC 地址而非 IP 地址,而将已知 IP 地址转换为 MAC 地 址的工作是由 ARP 协议来完成的。
HTTP2 与 HTTP1 区别
数据库事务
一.事务
定义:所谓事务,它是一个操作序列,这些操作要么都执行,要么都不执行,它是一个不可分 割的工作单位。
二.ACID
ACID,是指在可靠数据库管理系统(DBMS)中,事务(transaction)所应该具有的四 个特性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性 (Durability).这是可靠数据库所应具备的几个特性.
原子性 是指事务是一个不可再分割的工作单位,事务中的操作要么都发生,要么都不发生。
一致性:是指在事务开始之前和事务结束以后,数据库的完整性约束没有被破坏。
隔离性:多个事务并发访问时,事务之间是隔离的,一个事务不应该影响其它事务运行。
持久性,意味着在事务完成以后,该事务所对数据库所作的更改便持久的保存在数据库之中, 并不会被回滚。 .事
务之间的相互影响 隔离性:
脏读
丢失更新
不可重复读
幻读
并发模型
更新冲突检测
进程和线程
进程是对运行时程序的封装,是系统进行资源调度和分配的的基本单位,实现了操作系统的 并发;
线程是进程的子任务,是 CPU 调度和分派的基本单位,用于保证程序的实时性,实现进程 内部的并发;
区别:
1. 一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。线程依 赖于进程而存在。
2. 进程在执行过程中拥有独立的内存单元,而多个线程共享进程的内存。
3. 进程是资源分配的最小单位,线程是 CPU 调度的最小单位;
4 系统开销: 由于在创建或撤消进程时,系统都要为之分配或回收资源。因此,操作系统 所付出的开销将显著地大于在创建或撤消线程时的开销。在进行进程切换时,涉及到整 个当前进程 CPU 环境的保存以及新被调度运行的进程的 CPU 环境的设置。而线程切换 只须保存和设置少量寄存器的内容,进程切换的开销也远大于线程切换的开销。
5. 进程间不会相互影响。
6. 进程适应于多核、多机分布;线程适用于多核
进程间通信的方式: 进程间通信主要包括管道、系统 IPC(包括消息队列、信号量、信号、共享内存等)、以 及套接字 socket。
管道:管道主要包括无名管道和命名管道:管道可用于具有亲缘关系的父子进程间 的通信,有名管道除了具有管道所具有的功能外,它还允许无亲缘关系进程间的通信
1) 管道是半双工的(即数据只能在一个方向上流动),具有固定的读端和写 端
2)它只能用于具有亲缘关系的进程之间的通信(也是父子进程或者兄弟进程 之间)
3)它可以看成是一种特殊的文件,对于它的读写也可以使用普通的 read、 write 等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存 在于内存中。
命名管道 FIFO:
1)FIFO 可以在无关的进程之间交换数据
2)FIFO 有路径名 与之相关联,它以一种特殊设备文件形式存在于文件系统中
线程间通信的方式:
临界区:通过多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问;
互斥量 Synchronized/Lock:采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资 源的权限。因为互斥对象只有一个,所以可以保证公共资源不会被多个线程同时访问
信号量 Semphare:为控制具有有限数量的用户资源而设计的,它允许多个线程在同一时刻 去访问同一个资源,但一般需要限制同一时刻访问此资源的最大线程数目。
事件(信号),Wait/Notify:通过通知操作的方式来保持多线程同步,还可以方便的实现多线 程优先级的比较操作
协程和线程区别
那和多线程比,协程最大的优势就是协程极高的执行效率。因为子程序切换不是线程切换, 而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性 能优势就越明显。
第二大优势就是不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在 协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多
悲观锁
总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿 数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁
乐观锁
总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上 锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,
请问怎么实现线程池
参考回答: 1.设置一个生产者消费者队列,作为临界资源 2.初始化 n 个线程,并让其运行起来,加锁去队列取任务运行 3.当任务队列为空的时候,所有线程阻塞 4.当生产者队列来了一个任务后,先对队列加锁,把任务挂在到队列上,然后使用条件变量 去通知阻塞中的一个线程 .
死循环+来连接时新建线程的方法效率有点低,怎么改进?
参考回答: 提前创建好一个线程池,用生产者消费者模型,创建一个任务队列,队列作为临界资源,有 了新连接,就挂在到任务队列上,队列为空所有线程睡眠。 改进死循环:使用 select epoll 这样的技术。
请你说一下多线程的同步,锁的机制
参考回答: 同步的时候用一个互斥量,在访问共享资源前对互斥量进行加锁,在访问完成后释放互斥量 上的锁。对互斥量进行加锁以后,任何其他试图再次对互斥量加锁的线程将会被阻塞直到当 前线程释放该互斥锁。如果释放互斥锁时有多个线程阻塞,所有在该互斥锁上的阻塞线程都 会变成可运行状态,第一个变为运行状态的线程可以对互斥量加锁,其他线程将会看到互斥 锁依然被锁住,只能回去再次等待它重新变为可用。在这种方式下,每次只有一个线程可以 向前执行。
请你说一说死锁产生的必要条件?
参考回答: 1. 互斥条件:一个资源每次只能被一个进程使用。
2. 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
3. 不剥夺条件: 进程已获得的资源,在末使用完之前,不能强行剥夺。
4. 循环等待条件: 若干进程之间形成一种头尾相接的循环等待资源关系。
字节内推:
可以加好友随时查看状态。
各种复习资料也有
全部评论
(1) 回帖