来了来了他来了 C++后台面经倾情奉献
楼主20年应届生,学习方向主要是C++后端,去年春招面试过阿里钉钉,腾讯和华为,其中获得钉钉和华为实习offer,选择了钉钉并成功转正,腾讯在面了六七面后挂在了部门交叉面(我真不知道腾讯怎么这么多面,心力交瘁🤣)。分享一些面试中遇到的问题和有用的知识,部分解答来自当时的笔记,因为时间过去一年啦,想起什么写什么,以下不分先后顺序不分公司哦,供大家参考~
- 本面经的第一条建议就是:多看面经,你会发现面试中的问题或多或少都曾出现在别人的面经中。感谢牛客网在我找实习期间提供的帮助,面经真的帮了我很多!!
- TCP与UDP的区别(高频)
- TCP怎么保证数据的可靠传输?
序号,确认,超时和重传
- 怎么解决TCP的粘包问题?
我认为这本质不是一个问题,因为TCP本来就是一个字节流协议。解决方法:
1. 数据带固定的开始符和结束符
2. 数据长度固定
- TCP连接的建立为什么是三次握手而不是两次握手?
网络上关于这个问题的解答有很多个版本,贴一个我比较认可的:为了实现可靠数据传输, TCP 协议的通信双方, 都必须维护一个序列号, 以标识发送出去的数据包中, 哪些是已经被对方收到的。 三次握手的过程即是通信双方相互告知序列号起始值, 并确认对方已经收到了序列号起始值的必经步骤。如果只是两次握手, 至多只有连接发起方的起始序列号能被确认, 另一方选择的序列号则得不到确认。
- TCP状态转移图
- TCP中断连接时为什么要有time_wait状态?
- 大端和小端的区别?网络字节序是哪一个?
- 哪些排序算法是稳定的?哪些不稳定?
堆排序、快速排序、希尔排序、选择排序不是稳定的排序算法,而基数排序、冒泡排序、插入排序、归并排序是稳定的排序算法。
- 什么是乐观锁和悲观锁?
乐观锁:总是认为不会产生并发问题,每次去取数据的时候总认为不会有其他线程对数据进行修改,因此不会上锁,但是在更新时会判断其他线程在这之前有没有对数据进行修改,一般会使用版本号机制或CAS操作实现。
悲观锁:每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会被阻塞直到拿到锁。
- 为什么要有字节对齐?
Such alignment restrictions simplify the design of the hardware forming the interface between the processor and the memory system.
For example, suppose a processor always fetches 8 bytes from memory with an address that must be a multiple of 8. If we can guarantee that any double will be aligned to have its address be a multiple of 8, then the value can be read or written with a single memory operation. Otherwise, we may need to perform two memory accesses, since the object might be split across two 8-byte memory blocks.
On the other hand, some of the SSE instructions for implementing multimedia operations will not work correctly with unaligned data.
- 怎么控制字节对齐?
#pragma pack(n) 相关预编译指令。
基于字节对齐的存在,不能用memcmp比较两个结构体是否相等,因为对齐填充区域的值是随机的。
- 进程间通信方式
管道,命名管道,消息队列,共享内存,信号,信号量,套接字。
- epoll和select/poll的区别
- epoll的工作原理
epoll是通过内核与用户空间mmap同一块内存实现的。mmap将用户空间的一块地址和内核空间的一块地址同时映射到相同的一块物理内存地址(不管是用户空间还是内核空间都是虚拟地址,最终要通过地址映射映射到物理地址),使得这块物理内存对内核和对用户均可见,减少用户态和内核态之间的数据交换。内核可以直接看到epoll监听的句柄,效率高。
红黑树将存储epoll所监听的套接字。上面mmap出来的内存如何保存epoll所监听的套接字,必然也得有一套数据结构,epoll在实现上采用红黑树去存储所有套接字,当添加或者删除一个套接字时(epoll_ctl),都在红黑树上去处理,红黑树本身插入和删除性能比较好,时间复杂度O(logN)。
通过epoll_ctl函数添加进来的事件都会被放在红黑树的某个节点内,所以,重复添加是没有用的。当把事件添加进来的时候时候会完成关键的一步,那就是该事件都会与相应的设备(网卡)驱动程序建立回调关系,当相应的事件发生后,就会调用这个回调函数,该回调函数在内核中被称为:ep_poll_callback,这个回调函数其实就所把这个事件添加到rdllist这个双向链表中。一旦有事件发生,epoll就会将该事件添加到双向链表中。那么当我们调用epoll_wait时,epoll_wait只需要检查rdlist双向链表中是否有存在注册的事件,效率非常可观。这里也需要将发生了的事件复制到用户态内存中即可。
- C++基类的析构函数是否可以声明为virtual?
必须声明为virtual。
- C++析构函数是否可以抛异常?
如果对象在运行期间出现了异常,C++异常处理模型有责任清除那些由于出现异常所导致的已经失效了的对象(也即对象超出了它原来的作用域),并释放对象原来所分配的资源,这就是调用这些对象的析构函数来完成释放资源的任务,所以从这个意义上说,析构函数已经变成了异常处理的一部分。当无法保证在析构函数中不发生异常时,该怎么办?其实还是有很好办法来解决的。那就是把异常完全封装在析构函数内部,决不让异常抛出函数之外。
- C++有哪些转换?有什么区别?
static_cast, dynamic_cast, interpret_cast, const_cast
- C++STL容器的使用方法和底层原理(高频)
包括vector, deque, queue, stack, set, map, unordered_set, unordered_map等的使用和底层实现原理。
- 虚函数的使用场景和底层原理(高频)
- 介绍一下红黑树(高频)
- const与#define的区别
通常来说const优于#define。详情自行百度。
- 时间轮原理
来自腾讯,可能游戏后台需要这方面的知识。详情百度。
- 谈谈对操作系统的理解
回答大意:本质上程序在没有操作系统的计算机上也能正常运行,但编写程序变得异常麻烦。操作系统帮助管理硬件资源,并向上提供抽象,如进程是对CPU的抽象,文件是对磁盘的抽象,虚拟内存是对内存和磁盘的抽象。
- 静态库和动态库的区别
- 说一下从源代码到可执行程序的编译过程
- Linux系统向用户提供申请内存的有brk、sbrk和mmap函数。(malloc和new的底层函数)
- 函数调用步骤
参数入栈:将参数从右向左依次压入系统栈中
返回地址入栈:将当前代码区调用指令的下一条指令地址压入栈中,供函数返回时继续执行
代码区跳转:处理器从当前代码区跳转到被调用函数的入口处
栈帧调整,具体包括:
保存当前栈帧状态值,以备后面恢复本栈帧时使用。(EBP入栈)
将当前栈帧切换到新栈帧。(将ESP值装入EBP,更新栈帧底部)
给新栈帧分配空间。(把EBP减去所需空间的大小,抬高栈顶)
- 面向对象设计原则(华为)
1. 依赖倒置原则
2. 开放封闭原则
3. 单一职责原则
4. Liskov原则
5. 接口隔离原则
6. 优先使用对象组合,而不是类继承
7. 封装变化点
8. 针对接口编程,而不是针对实现编程
- 手写快速排序
- 手写memcpy函数
- 手写算法题:用两个栈实现一个队列 & 用两个队列模拟一个栈
- 手写算法题:查找二叉树中任意两个节点的最底层公共父节点
- 手写算法题:找出两个链表的第一个公共结点(提示:有没有发现和上面那道题的共同点)
- 定义一个不能被继承的类(来自腾讯,印象深刻,没答上来)
template <typename T> class MakeFinal{ friend T; private: MakeFinal() {} ~MakeFinal() {} }; class FinalClass : virtual public MakeFinal<FinalClass>{ public: FinalClass() {} ~FinalClass() {} };
- GDB调试程序的使用
- 查看程序运行所需的共享库:ldd命令
- 查看cpu使用情况:top命令
- 介绍一下grep, cut, sed, awk命令
- 智力题
- 实在想不起来更多了,还有很多跟个人项目相关的问题这里就不一一详述了,最后给大家推荐几本书,觉得对面试挺有帮助的。
《Linux多线程服务端编程》个人感觉对我帮助非常大,面试钉钉的时候一直在聊这本书上的内容。
《STL源码剖析》可以对STL容器的底层实现有一个非常清楚的认识。
《effctive c++》面试中出现的许多问题可以从中找到答案。
《剑指offer》这本书上的每一道题leetcode上面都有,属于中等偏低难度的题,前面讲过,面试中不会让你手写特别复杂的算法题,所以这本书刚好合适。
- 一点小技巧
面试中遇到不懂的问题不用慌,可以先诚恳地面试官表示不懂,然后可以结合已有知识对其进行一个合理推测,向面试官展示你在遇到问题时解决的思路,回答得好或许还很加分哦。工作中遇到不懂的问题是很正常的,解决不懂的问题也是一项重要的技能哦,面试官很好说话的,可能聊着聊着这面试就通过了。