字节跳动 C++后端 今日头条 校招面经答案
本文面经来自
字节跳动 C++后端 今日头条 校招
vector尾部添加元素,需要连续的内存空间吗?
需要,vector底层实现原理为一维数组(元素在空间中连续存放)
1.新增元素:
vector通过一个连续的数组存放元素,如果集合已满,再新增数据的时候,就要分配一个更大的内存,原来的数据复制过来,然后将原来内存释放,再插入新的元素。可以通过push_back和迭代器在任何位置插入,通过迭代器插入,知道第一个元素与迭代器当前位置的距离。这个位置之后的所有元素向后移动一个位置,再空出来的位置上存入新增的元素
2.删除元素:
删除和新增差不多,删除最后1个元素pop_back和通过迭代器删除任意一个元素erase,使用erase后,后面的每个元素的迭代器都会失效,后面的元素都往前移动一位,erase返回下一个有效的迭代器
C++程序到可执行文件的过程
动态链接和静态链接有什么区别,一般什么情况用动态链接,什么时候用静态链接?
C++和c语言相似,一个C++程序从源码到执行文件,有四个过程:预编译,编译,汇编,链接
预编译:
- 将所有的#define删除,并且展开所有的宏定义
- 处理条件预编译指令和#include指令,将被包含的文件插入到预编译指令的
- 过滤所有的注释
- 添加行号和文件名标识
编译:
- 词法分析:将源代码的字符序列分割成一系列记号
- 语法分析:对记号进行语法分析,产生语法树
- 语义分析:判断表达式是够有意义
- 代码优化
- 目标代码生成:生成汇编代码
- 目标代码优化
汇编:
将汇编代码转变为机器可以执行的指令
链接:
将不同的源文件产生的目标文件进行链接,从而实现一个可以执行的程序
链接可以分为动态链接和静态链接:
静态链接:
在链接的时候就已经把要调用的函数或者过程链接到了生成的可执行文件中,就算你去把静态库删除也不会影响可执行文件的执行。生成的静态链接库,Windows下以.lib为后缀,Linux下以.a为后缀。
动态链接:
在链接到时候没有把调用的函数链接进去,而是在执行过程中,再去找要链接的函数,生成的可执行文件中没有函数代码,只包含函数的重定位信息,所以当你删除动态库时,可执行文件就不能运行。生成的动态链接库,Windows下以.dll为后缀,Linux下以.so为后缀。
静态链接占用内存大,但是运行速度较快,效率高
动态链接占用内存较小,但是运行速度没有静态链接块。
C++程序内存布局是怎样的?堆和栈有什么区别?堆和栈各有什么优缺点?栈空间大小?
内存模型(内存布局),如下图所示:
从低地址到高地址,一个程序由代码段,数据段,BSS端,堆,共享区,栈等组成。
- 代码段:存放程序执行代码的一块内存区域。只读,代码段的头部还会包含一些只读的常数变量
- 数据段:存放程序中已经初始化的全局变量和静态变量的一块内存区域。
- BSS段:存放未初始化的或者初始化为0的全局变量和静态变量的一块内存区域
- 可执行程序在执行时又会多出两个区域:堆区和栈区
堆区:动态申请内存用,堆从低地址向高地址增长
栈区:存储局部变量,函数参数值,由高地址向低地址增长。是一块连续的空间
5.最后还有一个共享区,位于堆和栈之间
Linux下,默认栈空间大小为8MB
2GB内存的操作系统中,能否分配4GB的数组?
如果2GB内存指的是物理内存,那么就要区分是32位系统还是64位操作系统,
32
位系统的内核空间占用1G
,位于最高处,剩下的3G
是用户空间;64
位系统的内核空间和用户空间都是128T
,分别占据整个内存空间的最高和最低处,剩下的中间部分是未定义的。
32位操作系统最多申请出3g虚拟内存,所以是不可以的
64位操作系统最多申请出128T虚拟内存,则可以
如果2G内存指的是虚拟内存,直接不可以
TCP协议了解吗?怎么保证可靠性?按序到达怎么做到的?
TCP为了保证可靠性,采用了三次握手,四次挥手等机制,除此之外还有滑动窗口和拥堵控制算法。最最关键的是还保留了超时重传的机制。对于每份报文也存在校验,保证每份报文的可靠性。
用序列号保证按序到达:在建立连接时初始化一个随机数,通过SYN包传给接收端主机,每发送一次数据,就累加一次该数据字节数的大小。用来解决网络包乱序的问题。
TCP通讯时,服务端的程序挂掉了,客户端会怎么样?
有一个关于客户端挂掉的解释,和问题原理相同:
- 如果有数据传输:
在客户端拔掉网线后,服务端向客户端发送的数据报文会得不到任何的响应,在等待一定时长后,服务端就会触发超时重传机制,重传未得到响应的数据报文
如果在服务端重传报文的过程中,客户端刚好把网线插回去了,由于拔掉网线并不会改变客户端的 TCP 连接状态,并且还是处于 ESTABLISHED 状态,所以这时客户端是可以正常接收服务端发来的数据报文的,然后客户端就会回 ACK 响应报文。
此时,客户端和服务端的 TCP 连接依然存在的,就感觉什么事情都没有发生。
但是,如果如果在服务端重传报文的过程中,客户端一直没有将网线插回去,服务端超时重传报文的次数达到一定阈值后,内核就会判定出该 TCP 有问题,然后通过 Socket 接口告诉应用程序该 TCP 连接出问题了,于是服务端的 TCP 连接就会断开。
而等客户端插回网线后,如果客户端向服务端发送了数据,由于服务端已经没有与客户端相同四元祖的 TCP 连接了,因此服务端内核就会回复 RST 报文,客户端收到后就会释放该 TCP 连接。
此时,客户端和服务端的 TCP 连接都已经断开了。
- 如果拔掉网线后,没有数据传输
针对拔掉网线后,没有数据传输的场景,还得看是否开启了 TCP keepalive 机制 (TCP 保活机制)。
如果没有开启 TCP keepalive 机制,在客户端拔掉网线后,并且双方都没有进行数据传输,那么客户端和服务端的 TCP 连接将会一直保持存在。
而如果开启了 TCP keepalive 机制,在客户端拔掉网线后,即使双方都没有进行数据传输,在持续一段时间后,TCP 就会发送探测报文:
- 如果对端是正常工作的。当 TCP 保活的探测报文发送给对端, 对端会正常响应,这样 TCP 保活时间会被重置,等待下一个 TCP 保活时间的到来。
- 如果对端主机崩溃,或对端由于其他原因导致报文不可达。当 TCP 保活的探测报文发送给对端后,石沉大海,没有响应,连续几次,达到保活探测次数后,TCP 会报告该 TCP 连接已经死亡。
操作系统中一个进程要删除正在被写入的文件,能不能删除成功?
在Linux系统中能够成功。
Linux中真正表示一个文件的是inode索引节点,而文件名只是指向了inode(自然能够想到可以有多个文件名指向同一个inode,这就是文件系统中的硬连接)。用rm -f这样的命令删除文件,其实是删除了文件名到inode之前的连接关系,那么直接表示文件内容的inode是什么时候删除的呢?答案是文件系统判断如果所有由文件名指向inode的关系都被删除的时候,就把inode删除掉。
如果未给文件建立硬链接,也就是inode应该马上被删除,inode都没有了,删除应该报错才是啊,其实文件系统在回收inode时,除了判断到inode的链接数为0,还要判断到inode的引用数为0,当一个进程打开一个文件时,它的inode的引用数就会加1,所以这里虽然链接数为0但引用数为1,文件系统还是不会删除inode,那么什么时候删除inode呢,答案是进程结束时引用数归0时。这也是为什么,有时我们将一个文件删除了,但是用du命令查看空间并没有被释放,重启程序空间就释放了的原因。