1. 为什么这三块知识总被放在同一张面试桌上如果你翻过几十份C/C方向的招聘要求会发现一个很固定的组合语言基础、操作系统、计算机网络。很多人把这三样当成三门独立的课去背结果面试时被追问两句就露馅。真正的问题在于面试官考的不是你记住了多少结论而是你能不能把这三层知识串成一条线。举个最典型的例子。面试官问“一个TCP连接上发送数据从调用send到对方recv中间发生了什么”这个问题表面是网络题实际上要你讲清楚用户态到内核态的切换、协议栈的缓冲区管理、网卡中断处理、进程调度唤醒。你只背TCP三次握手这题就答不下去。我见过太多候选人C语言语法题做得飞快指针、内存对齐、虚函数表都能说但一被问到“malloc申请1字节实际占多少内存”“为什么fork之后父子的文件描述符指向同一个文件表项”就卡住。原因很简单他们把C语言当成纯语法在学没有和操作系统挂钩。所以这篇面经不是给你一份背诵清单。我想做的是把这三块知识按“面试官会怎么串着问”的逻辑重新组织一遍每一块都讲清楚底层原理、常见追问方向、以及我实际面试中踩过的坑。适合准备校招或社招C/C岗位的人也适合工作几年但基础有些模糊的开发者拿来查漏补缺。2. C/C语言层面面试官真正想筛掉的是什么人2.1 指针与内存模型从“会用”到“说得清”指针题几乎是必考但面试官早就过了问“指针和引用区别”的阶段。现在更常见的是给一段代码让你判断行为比如char *p hello; char q[] hello;这两行的区别能答出“p指向只读数据段q是栈上数组可修改”只是及格。追问会接着来sizeof(p)和sizeof(q)分别是多少p合法吗q呢如果对p做写操作会发生什么这一串问下来考的是你对内存分区的理解不是指针语法。再往深走面试官会问结构体内存对齐。很多人背过“按最大成员对齐”但实际规则要复杂一些。以64位系统为例struct A { char a; int b; char c; };sizeof(struct A)是多少答案是12不是9。原因是int要求4字节对齐a后面补3字节c后面再补3字节保证整体是4的倍数。如果换成double成员对齐边界变成8结果又不一样。这个知识点面试官喜欢结合#pragma pack一起问考察你知不知道怎么控制对齐、为什么要控制。实操心得回答内存对齐时主动说出“对齐是为了让CPU一次总线周期就能读到数据未对齐访问在某些架构上会触发异常或性能下降”比单纯背规则加分很多。2.2 虚函数与对象模型别只背“多态”两个字C岗位必问虚函数。基础问法是“虚函数怎么实现多态”标准答案是虚函数表加虚表指针。但面试官真正想区分水平的地方在于细节虚表指针在对象内存布局的哪个位置构造期间虚表指针怎么变化构造函数能不能是虚函数为什么析构函数为什么建议声明为虚函数什么情况下不需要多重继承下虚表怎么组织菱形继承的虚基类表又是什么我印象很深的一次面试面试官让我写出下面代码的输出class Base { public: Base() { foo(); } virtual void foo() { cout Base::foo endl; } }; class Derived : public Base { public: void foo() override { cout Derived::foo endl; } }; Derived d;答案是Base::foo。原因是构造基类部分时虚表指针还指向基类的虚表派生类还没构造完多态不会生效。这个点如果没踩过坑很难答对。2.3 智能指针与RAII现代C的分水岭现在面试如果还只写裸指针管理资源基本会被认为停留在旧标准。unique_ptr、shared_ptr、weak_ptr的考察点集中在智能指针所有权语义引用计数典型追问unique_ptr独占无怎么转移所有权能放进容器吗shared_ptr共享有循环引用怎么破线程安全吗weak_ptr不增加计数无怎么提升为shared_ptrshared_ptr的线程安全问题特别容易被问。控制块的引用计数是原子操作线程安全的但被管理对象的读写不是线程安全的。这个区分很多人答不上来。还有enable_shared_from_this的使用场景为什么不能在构造函数里调用shared_from_this()这些都是实战中才会遇到的坑。2.4 我踩过的C面试坑有一次被问到“std::move到底做了什么”。我回答“把左值转成右值”面试官追问“它移动了什么内存吗”。其实std::move本身不移动任何东西它只是一个类型转换真正的移动发生在移动构造或移动赋值里。这个理解偏差让我当时很尴尬。还有一次问“vector扩容为什么是2倍或1.5倍”。除了摊还分析的时间复杂度面试官还想听你讲内存碎片和缓存友好性。1.5倍相比2倍更容易复用之前释放的内存块减少碎片。这种细节才是拉开差距的地方。3. 操作系统从进程线程到内存管理的追问链3.1 进程与线程面试官爱问的“边界情况”“进程和线程区别”是入门题但追问往往从这里开始分叉。常见追问链是这样的线程共享哪些资源独享哪些线程切换和进程切换开销差在哪协程和线程的区别为什么协程切换更轻一个进程最多能创建多少线程受什么限制第4个问题很能看出功底。答案不是固定的受虚拟内存空间、线程栈大小、系统参数限制。32位系统用户态地址空间约3GB默认线程栈8MB理论上限约380个左右但实际远小于这个数因为还有堆、代码段等占用。64位系统地址空间大得多限制主要来自物理内存和内核参数。3.2 内存管理虚拟内存不是“虚拟的”虚拟内存是操作系统面试的核心。面试官喜欢从malloc一路问到页表malloc(1)和malloc(4096)在底层行为上有什么区别什么是缺页中断发生缺页中断时操作系统做了什么页表多大多级页表怎么节省空间TLB是什么为什么它能加速地址转换malloc小内存时从堆的空闲链表分配大内存通常超过128KB会直接用mmap映射一块匿名区域。这个阈值可以通过mallopt调整。缺页中断的处理流程是CPU访问虚拟地址MMU查页表发现页不在内存触发中断内核分配物理页、从磁盘读入、更新页表、返回用户态重新执行指令。多级页表的设计逻辑值得展开。32位系统二级页表顶级页表1024项每项指向一个二级页表二级页表1024项每项映射4KB页。这样只有实际使用的地址区间才需要二级页表节省大量空间。64位系统通常用四级或五级页表。3.3 进程间通信与同步别只会说“管道、消息队列、共享内存”IPC的考察重点不是列举而是选型理由和底层实现。比如管道为什么是半双工的匿名管道和命名管道区别在哪共享内存为什么是最快的IPC它的同步问题怎么解决信号和信号量的区别信号处理函数有什么限制共享内存快是因为数据不需要在用户态和内核态之间拷贝直接映射同一块物理内存。但多个进程同时读写需要额外同步通常配合信号量或互斥锁使用。信号处理函数的限制很多人不知道它必须是可重入的不能调用非异步信号安全的函数比如printf、malloc都不安全。3.4 调度与锁从自旋锁到无锁队列锁的问题是C后端面试的高频区。面试官会从“互斥锁和自旋锁区别”开始一路问到“什么是ABA问题”“内存序有哪几种”。自旋锁适合临界区极短的场景避免线程切换开销互斥锁适合临界区较长或可能睡眠的场景。自旋锁在单核上没意义因为自旋的线程占着CPU持有锁的线程没机会运行。无锁编程的追问更深。CAS操作的ABA问题是指线程1读到值A准备CAS改成C期间线程2把A改成B又改回A线程1的CAS成功但中间状态被忽略了。解决方案是加版本号或使用带标记的指针。内存序方面C11提供了六种memory_order从relaxed到seq_cst理解它们需要清楚编译器和CPU的重排序规则。4. 计算机网络从三次握手到协议栈全链路4.1 TCP连接管理握手挥手背后的状态机三次握手和四次挥手是必背但面试官现在更爱问“为什么”为什么是三次握手而不是两次两次会有什么问题为什么挥手是四次TIME_WAIT状态的作用是什么持续多久如果客户端最后不发送ACK会怎样两次握手的问题在于服务端无法确认客户端的接收能力。如果客户端的历史连接请求延迟到达服务端会误以为新连接建立浪费资源。TIME_WAIT持续2MSL报文最大生存时间作用是确保最后的ACK能到达服务端以及让本次连接的旧报文在网络中消散避免影响新连接。状态转换图要能画出来。CLOSE_WAIT状态过多通常意味着应用层没有正确关闭连接TIME_WAIT过多则可能是短连接频繁创建导致。这些实战排查经验面试官很看重。4.2 TCP可靠传输滑动窗口、拥塞控制、重传可靠传输的机制要能串起来讲滑动窗口怎么实现流量控制拥塞控制有哪几个阶段慢启动、拥塞避免、快重传、快恢复分别做什么超时重传的时间怎么计算滑动窗口的本质是接收方告诉发送方“我还能收多少”发送方根据这个调整发送速率。拥塞控制则是发送方根据网络状况自我调节。慢启动阶段窗口指数增长到达阈值后进入拥塞避免线性增长检测到丢包时根据情况进入快重传或超时重传。RTO的计算用Jacobson算法综合平滑RTT和RTT偏差。这个算法保证了重传时间既不过于激进也不过于保守。4.3 从输入URL到页面展示全链路串讲这是网络面试的经典大题也是检验你能否把各层知识串起来的最好方式。完整链路包括DNS解析浏览器缓存、系统缓存、递归查询TCP连接三次握手TLS握手如果是HTTPS证书验证、密钥交换HTTP请求请求行、请求头、请求体服务端处理负载均衡、反向代理、应用逻辑HTTP响应状态码、响应头、响应体浏览器渲染HTML解析、CSSOM、渲染树、布局、绘制每一层都能展开追问。比如DNS用UDP还是TCP为什么TLS握手用了几次RTTTLS1.3怎么优化到1-RTT甚至0-RTTHTTP/2的多路复用怎么解决队头阻塞这些问题层层递进能答到哪一层直接反映你的知识深度。4.4 网络编程实战epoll与IO多路复用C后端岗位几乎必问epoll。面试官会从“select、poll、epoll区别”开始深入到epoll的水平触发和边缘触发区别边缘触发为什么必须配非阻塞IOepoll的红黑树和就绪链表怎么工作惊群问题是什么怎么解决边缘触发下如果一次没读完数据后续不会再次通知所以必须循环读到EAGAIN。非阻塞IO保证读操作不会阻塞住整个线程。epoll内部用红黑树管理监听的fd用就绪链表存放已触发的事件epoll_wait直接检查就绪链表不需要遍历所有fd这是它比select高效的根本原因。惊群问题在多个进程或线程同时等待同一个epoll实例时出现。解决方案包括使用EPOLLEXCLUSIVE标志、或者只让一个线程负责accept。5. 把三块知识串起来的面试实战策略5.1 面试官怎么设计连环追问面试官的追问不是随机的通常沿着“语言→系统→网络”或者反向的链路走。比如从shared_ptr的线程安全问到原子操作的实现再问到CPU缓存一致性协议最后问到网络编程中多线程模型的选型。这条链路考的是你能否把不同层次的知识关联起来。我总结了几条常见的追问链路起点中间追问终点malloc虚拟内存、缺页中断页表、TLB虚函数对象内存布局构造析构顺序TCP握手状态机、TIME_WAIT端口耗尽排查epoll红黑树、就绪链表多线程Reactor模型智能指针引用计数原子性内存序、无锁编程准备的时候按这些链路去串比孤立地背知识点有效得多。5.2 回答节奏与深度控制面试中的回答节奏很关键。我的经验是先给结论再展开一层原理然后主动留一个“钩子”让面试官追问。比如被问到“进程和线程区别”先答“线程是调度的基本单位进程是资源分配的基本单位”然后补一句“线程切换不需要切换页表所以开销更小”面试官很可能顺着问“那协程呢”。这样你就把节奏握在自己手里。但要注意别过度展开。如果面试官只是随口一问你滔滔不绝讲五分钟反而显得不会抓重点。观察面试官的反应如果他继续追问细节就深入如果他点头转向下一个问题就收住。5.3 我实际面试中遇到的“送命题”有一次面试官问“你刚才说epoll边缘触发必须配非阻塞IO那如果我就是用阻塞IO会怎样”这个问题很刁钻。阻塞IO下边缘触发通知后你循环读最后一次读会阻塞住因为没数据了但fd是阻塞的。整个线程卡死其他连接得不到处理。这个场景如果你没实际写过代码很难答得具体。还有一次问“shared_ptr的引用计数是原子的那它线程安全吗”标准答案是引用计数的增减是线程安全的但被管理对象的读写不是。如果两个线程同时读同一个shared_ptr指向的对象没问题如果一个读一个写需要额外同步。这个区分我见过至少一半候选人答错。5.4 复习路径与资料选择最后说下复习路径。我的建议是按“链路”复习不要按“科目”复习。具体做法拿一张白纸画出从C代码到网络包的完整路径每个节点问自己三个问题是什么、为什么这样设计、有什么替代方案找几个开源项目比如简单的HTTP服务器读源码看这些知识怎么落地模拟面试找人互相追问训练表达节奏资料方面经典书要读但别只读书。操作系统可以看《操作系统导论》网络可以看《TCP/IP详解》C可以看《深度探索C对象模型》。但更重要的是动手写代码验证比如自己实现一个简单的线程池、一个基于epoll的服务器写的过程中会遇到各种书上没讲的问题这些才是面试时能讲出彩的素材。注意面试中遇到不会的问题不要硬编。诚实说“这块我了解不深但我的理解是……”然后给出你的推理过程。面试官更看重思考方式而不是标准答案。