腾讯后台开发面试核心考点:C++、TCP/IP与系统设计全面复盘 准备腾讯2015春招后台开发岗的时候我找了不少资料最后发现一套练习卷特别说明问题。那会儿腾讯后台的主流语言还是C面试官考的东西非常实在指针、内存、进程线程、TCP/IP、手写算法几乎全是后台日常要碰的内容。十年后再看题面虽然旧了但考察的底层能力一点没过时。下面我就按自己的拆解过程把这套卷子的考点、典型题、答题思路和踩坑经验完整过一遍。不管你是正在准备大厂后台岗的校招生还是想系统回炉基础知识的工作党都可以拿这份内容当复习地图用。1. 练习卷的整体设计逻辑它到底在筛什么人1.1 2015年前后的腾讯后台技术栈与用人标准2015年的背景是移动互联网全面爆发微信用户数已经迈过5亿门槛QQ空间、游戏后台的量级都很大后台服务的稳定性直接决定产品能不能跑起来。这种业务形态决定了后台岗位的用人标准上手就能写高效、稳定的服务出了线上问题能快速定位。所以那几年笔试的风格很统一就是考基本功——C语言机制、Linux环境、网络协议、数据结构。练习卷本质上是一张能力扫描图选择题考你是不是真的理解内存和并发编程题考你手写代码是否够快够准设计题考你有没有全局视角。这里有个容易被忽略的点腾讯后台岗位当年对C的重视程度超过一般互联网公司。原因不复杂大量核心服务消息收发、游戏后台、存储系统都是C写的内存和性能的掌控能力直接决定服务质量。所以练习卷里C相关题目的分值占比很高准备阶段必须把它当主菜而不是配菜。1.2 题型构成与合理的时间分配我翻过的练习卷版本不完全一样但题型骨架基本固定可以用下面这个表概括题型大致数量考察方向建议用时单选题15~20道C语法、OS概念、网络协议20分钟多选题5~8道对象模型、并发、复杂度边界10~15分钟简答题3~4道机制解释、原理说明25分钟编程题2~3道链表、树、字符串、场景算法40分钟系统设计题1道短链、缓存、存储架构15~20分钟如果整套卷子限时120分钟比较合理的时间分配是选择和多选30~35分钟简答25分钟编程题40分钟设计题15~20分钟留一点机动时间检查。我见过太多人把时间耗在选择题的纠结上最后编程题写不完。笔试的淘汰率一多半在编程题练习的时候就要养成这个节奏感。2. C/C语言考点基本功决定生死2.1 sizeof、指针与内存管理选择题的重灾区C基础部分的选择题非常有代表性我挑几类高频考点展开说一下。第一类是sizeof计算。练习卷里经常给你一个结构体让你算sizeof的值比如struct A { char a; // 1字节 int b; // 4字节 short c; // 2字节 };很多人直接算成7答案是12。原因是编译器做了内存对齐a占1字节后填充3字节b占4字节c占2字节后再填充2字节总大小对齐到最大成员对齐数4的整数倍也就是12。记忆点就一句话结构体大小是最大成员对齐数的整数倍每个成员起始偏移必须能被自身大小整除。为什么要对齐因为CPU访问内存时按字读取未对齐的数据可能导致两次总线访问性能开销明显。这不是编译器闲得没事是硬件层面的约束。第二类是指针和引用的区别。经典答法指针是变量存的是地址可以重新赋值可以为空引用是别名必须在定义时初始化不能改绑不存在空引用。考点背后的逻辑是C为什么要引入引用为了支持操作符重载和拷贝构造同时让函数传参更安全。展开到深浅拷贝这条线就引出下一类题。第三类是malloc/free与new/delete的异同。共同点是都管理堆内存区别在于new/delete会调用构造和析构函数而malloc/free只是分配和释放裸内存。另外new返回类型化指针malloc返回void*需要强转new失败抛bad_allocmalloc失败返回NULL。面试官常追问能不能混用答案是不能混用会导致要么构造函数没调用要么析构函数没调用后果是资源泄漏和未定义行为。2.2 虚函数、vptr与构造函数里别调虚函数对象模型是让很多人头疼的部分。练习卷里最常见的考法是给一个继承体系让你分析输出比如基类和派生类构造析构顺序、虚函数调用结果。class Base { public: virtual void f() { cout Base::f endl; } Base() { f(); } virtual ~Base() {} }; class Derived : public Base { public: virtual void f() { cout Derived::f endl; } Derived() { f(); } };构造Derived对象时输出是什么答案是Base::f和Derived::f。原因很底层对象构造时vptr的初始化顺序是先基类后派生类。执行Base构造函数时vptr指向的还是Base的虚表所以调用f()会走基类版本等进入Derived构造函数vptr才切到Derived的虚表。这也解释了为什么构造函数里不能调虚函数——调了也不是你想调的那个版本。类似的析构函数最好声明为虚函数。如果基类析构不是虚的通过基类指针delete派生类对象时只会调用基类析构派生类成员可能没被正确释放这是典型的未定义行为。当年简答题常考这两点回答时把vptr按构造顺序切换这个底层机制说出来分数会明显不一样。2.3 const、static与语言类答题模板const和static的考点比较碎但好拿分。const的几种用法要分清修饰变量表示只读修饰指针要分顶层和底层修饰成员函数表示不修改对象状态修饰函数参数表示入参只读。static有三层意思静态局部变量生命周期到程序结束但作用域不变静态全局变量限制只在当前编译单元可见静态成员函数不依赖对象不能访问非静态成员。答题时先讲语义再补一个实际场景比如用static修饰单例的实例指针。语言类题目的通用答题模板我总结为三步先说概念定义再说底层机制最后给一个生产场景。比如问引用和指针的区别先答定义层面的区别再提引用在底层也是指针编译器处理时用指针实现最后说所以写拷贝构造函数时参数用const引用既避免拷贝开销又防止修改外部对象。三步走下来即使回答不够精简也一定显得你理解到位。3. 数据结构与算法手写代码的硬功夫3.1 链表、二叉树与字符串编程题的常客那几年的编程题链表反转出现的频率高到像一个梗。题目很直接不借助额外空间反转单链表。迭代版的核心是维护三个指针ListNode* reverseList(ListNode* head) { ListNode* prev nullptr; ListNode* cur head; while (cur) { ListNode* next cur-next; cur-next prev; prev cur; cur next; } return prev; }递归版同样可以写但手写代码的场合我更推荐迭代版不需要考虑递归栈开销面试官读起来也直观。写完之后主动补充边界条件——空链表、只有一个节点这属于送分题但写漏了会被扣印象分。另一个高频题是二叉树层序遍历。核心是借助队列每轮先记录当前队列长度再逐层弹出这样可以天然按层分组。这题背后的考察点不只是BFS而是你有没有想过如何区分层边界。很多人只会无脑入队出队层与层混在一起这就是基本功不扎实的信号。字符串类题目里反转单词、最长无重复子串滑动窗口都是热点滑动窗口的双指针思想要练到肌肉记忆它几乎是字符串题的万能钥匙。3.2 TopK、bitmap与海量数据处理的基本功练习卷里还有一道很有代表性的题10亿个整数找出最大的100个。这题背后是海量数据处理的经典思路——不排序用小顶堆维护TopK堆大小为100遍历一遍数据比堆顶大就替换并调整堆时间复杂度O(n log k)。为什么不直接排序因为数据量一大内存根本放不下排序是全局操作而TopK只需要局部信息堆天然适合这种只关心边界的场景。类似的思路延伸到bitmap用每一位表示一个数的存在状态1亿个整数去重只要12.5MB内存比哈希表省几十倍。布隆过滤器则是用多个哈希函数映射到位数组上用来判断一定不存在和可能存在适合做缓存穿透的拦截层。这些考点在2015年的练习卷里就反复出现直到今天面试依然是高频题。核心不是背结论而是理解内存有限、数据量大这个约束下算法设计的取舍。3.3 手写代码的现场策略关于手写代码我有一条很实用的经验先写注释再写代码。把函数签名、输入输出、边界条件先以注释形式定下来相当于先画设计图再施工。这样即使写到一半卡住阅卷人也能看到你的思路步骤不会整题零分。再有就是代码风格。变量命名别用a、b、tmp这种写成prev、cur、next一眼能看出意义括号和缩进统一。很多笔试是人工阅卷一份清爽的代码和一份乱糟糟的代码即使算法正确分差也可能拉到10%。另外写完代码一定要检查一遍空指针和循环边界我统计过自己刷题时的错误一半以上出在边界条件。4. 操作系统与网络并发和TCP/IP的较量4.1 进程线程与死锁简答题的题库操作系统部分选择题和简答题最喜欢考进程线程的区别和死锁条件。进程和线程的区别标准答法是进程是资源分配的基本单位线程是CPU调度的基本单位同一进程的线程共享地址空间、文件描述符等资源而进程之间地址空间隔离线程创建和切换开销比进程小通信也更方便但共享带来的同步问题也更突出。追问为什么线程切换开销小要从上下文切换的角度答进程切换要切换页表、刷新TLB线程切换主要保存寄存器和栈指针不需要动地址空间。把这个底层机制答出来比背概念强得多。死锁部分四个必要条件是互斥、持有并等待、不可剥夺、循环等待。破坏任何一条都能预防死锁比如用trylock避免持有等待用超时机制实现可剥夺用资源有序分配打破循环等待。银行家算法是经典的避免死锁策略面试一般只要求说清思想在分配前判断系统是否仍处于安全状态安全才分配。4.2 TCP三次握手、四次挥手与TIME_WAIT网络部分绕不开TCP。三次握手的考察已经从背状态升级到理解为什么。为什么需要第三次握手核心原因是防止失效的连接请求突然到达服务端导致服务端建立一条客户端根本不存在的连接白白浪费资源。经典场景是客户端第一个SYN在网络中滞留重传后正常建立连接并关闭滞留的SYN又到了服务端如果没有第三次握手服务端就会误判。TIME_WAIT是另一个高频考点。主动关闭连接的一方在收到对端FIN并发出ACK后要进入TIME_WAIT并等待2MSL。为什么必须等一是确保最后的ACK能到达对端如果丢了对端会重传FIN此时还能重新发ACK二是让旧连接的所有报文在网络中自然消失避免污染新连接。生产环境经常出现大量TIME_WAIT连接常见处理是开启tcp_tw_reuse客户端场景有效、调整keepalive、缩短连接生命周期但从根上看要减少短连接的频繁创建比如做连接复用。4.3 select、poll与epoll网络编程的隐形考点网络编程简答题还喜欢考select、poll、epoll的区别我做了一张对比表方便记忆机制触发方式上限性能特点select轮询FD_SETSIZE限制默认1024每次都要遍历全部fdO(n)poll轮询无上限但需管理fd数组同样遍历全部fd量大时开销高epoll事件驱动回调仅受系统文件数限制只返回就绪fd适合海量连接把这几个差异说清楚面试官就知道你确实写过Linux下的高并发服务。并发模型方面练习卷也常让你比较多线程和多进程的适用场景我给一个实用结论IO密集的服务用多线程加事件循环epoll加线程池CPU密集的任务用多进程或线程绑核避免频繁切换。这个话题没有标准答案关键看你能不能结合场景说清楚取舍。5. 数据库与系统设计从索引到缓存5.1 MySQL索引为什么选B树数据库的核心考点里为什么索引用B树出现频率极高。答案要分两层为什么不用哈希为什么不用二叉树或B树。哈希索引只适合等值查询一旦做范围查询就只能全表扫描B树所有数据都存在叶子节点并且叶子节点之间用链表串起来天然支持范围查询和排序。对比B树B树的非叶子节点不存数据只有索引所以同样大小的节点能存更多索引项树更矮磁盘IO次数更少而且B树所有查询都要走到叶子节点查询时间稳定。把磁盘IO、树高、范围查询这条逻辑串起来就是一个很完整的答案。5.2 事务的ACID与隔离级别事务问题是简答题的保留项目。ACID四个性质要背更要能解释原子性要么全成功要么全回滚一致性事务前后数据满足约束隔离性事务之间相互隔离持久性提交后修改永久保存。面试官跟进问的是事务隔离级别分四档——读未提交、读已提交、可重复读、串行化分别解决脏读、不可重复读、幻读的问题。MySQL默认是可重复读但要注意可重复读在标准SQL定义下仍可能幻读InnoDB通过间隙锁在特定条件下避免了幻读。能把这层标准定义和InnoDB实现的区别讲清楚是很加分的地方。5.3 Redis与缓存三大问题2015年前后Redis已经成了后台架构的标配练习卷里的系统设计题通常都藏了缓存的考点。缓存穿透、击穿、雪崩是必须答出来的三个问题穿透是查一个不存在的key每次都要穿透到DB解决用布隆过滤器拦截或缓存空值击穿是热点key突然过期大量请求涌到DB解决用互斥锁或者让热点key不设过期时间后台异步更新雪崩是大面积key同时过期或Redis宕机解决有过期时间加随机值、做高可用集群、限流降级。这三个问题的答法体现了系统设计能力因为每个背后都是真实线上事故。我当时准备的技巧是每个问题都配一个我见过或我做过的真实场景来描述比如做过一个商品详情页的缓存热门商品缓存失效后DB瞬间被打满后来加了互斥锁只允许一个请求回源重建缓存。面试官要的不是你知道名词而是你有没有处理过同类问题的直觉。6. 一套自测题与参考答案6.1 五道自测题如果你想把前面内容落到笔头上我整理了五道有代表性的自测题可以限时40分钟做完再对照参考答案给自己打分。【C】A类含有一个int成员和一个char成员sizeof(A)为什么不是5解释内存对齐的原则并说明对齐带来的性能收益。【算法】给定单链表头指针原地反转链表要求写出完整代码并说明边界条件。【OS】简述进程和线程的区别并从上下文切换的角度说明为什么线程切换开销更小。【网络】解释TCP建立连接三次握手的原因并说明握手过程中两端的状态变化CLOSED、LISTEN、SYN_SENT、SYN_RCVD、ESTABLISHED。【系统设计】设计一个短链接服务要求说清存储方案、防冲突策略、重定向流程和过期清理策略。6.2 答案要点与踩分点第一题踩分点能说出最大成员对齐数填充字节硬件按字读取这三个关键词并算出正确的sizeof值。第二题踩分点三个指针是否正确维护空链表和单节点是否处理代码是否有明显编译错误。第三题踩分点资源分配单位vs调度单位、地址空间是否隔离、切换时是否动页表。第四题踩分点必须提到防止失效的历史SYN导致服务端误建连接只说确认双方收发能力只能得一半分因为那只是表面作用。第五题踩分点存储用自增ID或哈希短码都可以关键是说清短码如何保证唯一发号器、哈希碰撞检测、跳转用302还是301统计点击用302节省跳转用301、过期数据用惰性删除加定期清理。这五道题并不超纲但每一道都能拉开差距。自测时不要只看对不对要看回答里的信息量够不够能不能让一个没见过你的考官快速确认你理解了原理。7. 复盘后的几点经验7.1 高频丢分点概念都知道一写就露馅我把自己的练习卷和身边同学的错题放在一起复盘发现丢分点出奇地集中。第一个是知道但说不清比如很多人知道TIME_WAIT存在但说不清为什么必须是2MSL回答停留在背结论的层面。第二个是选择题能选对简答题写不完整说明知识是零散记忆没有形成体系。第三个是编程题边界条件丢分链表为空、参数为null这种平时刷题时少了check这个习惯。针对这三种症状我的建议是每复习完一个知识点用讲给新手听的方式复述一遍编程题刷完一道把边界条件单独列出来养成下意识检查的习惯。这套方法比再多刷三套题都管用。7.2 练习卷的正确用法不是背题是建立知识地图练习卷最大的价值不是猜中原题而是帮你把后台开发的知识点串成一张地图。我自己的做法是做完一套卷子后把每道题对应的知识点写到一张A4纸上用箭头连起来。比如内存对齐连到结构体连到缓存行性能优化TCP三次握手连到连接状态机连到短连接优化。画完之后会很直观地看到哪些地方你只有孤立节点哪些地方已经有了完整链路。我现在回看那张A4纸发现上面九成的知识点在工作中都真的用上了。后来我甚至把这种方法带到团队里新人入职第一周不做业务先按这套知识点自己画地图、自己讲一遍。对于后台开发这个工种能写代码只是起点能把运行机制讲清楚才是系统出了问题能找到根因的人。