
如果你正在准备深信服校园招聘 C/C 软件开发岗笔试大概率是你绕不过去的第一道坎。我当年拿到B卷时第一反应是去搜A卷和B卷的区别结果越搜越慌。后来把整套知识体系过了一遍才发现卷子代号变来变去真正想考察的底层能力其实非常稳定。这篇文章不打算讲任何一场考试的具体原题而是把B卷背后的考点、编程题应对模板、上机环境的坑和备考节奏都拆开梳理一遍。适合正在准备深信服校招的同学也适合所有秋招想冲网络安全、云计算方向的C/C岗位的求职者。1. B卷到底考什么岗位画像与笔试定位1.1 深信服C/C开发岗为什么考得“硬核”先看岗位本身。深信服的核心业务是网络安全、云计算、企业IT基础设施这几条产品线对C/C的依赖程度非常高。网络转发、协议解析、底层存储、虚拟化组件、安全引擎这些模块要的是对内存、性能、并发的深入理解而不是单纯会调用框架接口。所以校招笔试里C/C考题普遍偏“硬核”选择题有大量语言细节和操作系统原理编程题则重点考察算法实现能力。知道了这一点就不会奇怪为什么卷子里动不动就是指针、多线程、TCP状态机。说白了笔试筛的不是谁会用某个框架而是谁有扎实的计算机基础功。框架可以入职后再学但底层能力很难在短时间补上来企业把笔试作为第一道门槛也就顺理成章了。1.2 A/B卷的差异机制与备考心态再说回A/B卷。很多公司在同一场笔试里会配置多套卷子主要目的是防止前后交卷的同学串题。B卷和A卷的难度通常控制在同一个水平线题目内容不同但考察的知识点范围基本一致。所以如果你拿到的是B卷完全没必要紧张——它不代表“更难的版本”只是一份平行卷。我自己的做法是考前不去猜会抽到哪套卷而是把常考知识点整理成一张清单逐个击破。只要知识地图覆盖到位了卷子是A还是B没有任何区别。这个心态非常重要很多人在考场上发挥失常不是因为不会做而是因为一直在想“这套题怎么跟之前练的不太一样”导致手忙脚乱。准备充分的人眼里只有“会做的题”和“暂时不会的题”没有A卷和B卷之分。1.3 考察范围与知识地图根据我对同类岗位笔试的观察C/C软件开发岗的笔试内容大致可以分成四个模块模块核心考点常见题型重要性C/C语言指针、引用、虚函数、内存管理、STL、C11特性选择题、改错题高数据结构与算法链表、二叉树、哈希、排序、二分、动态规划、图论基础编程题高操作系统进程线程、锁、死锁、内存布局、IPC选择题中计算机网络TCP/UDP、三次握手、socket、HTTP基础选择题、简答中除了这四个模块Linux常用命令和编译调试知识也偶尔出现在选择题里。编程题一般2到3道题量不大但每道都有区分度考察的多是“能否用C/C写出高效、正确的代码”。从分值权重来看C/C语言基础通常出现在选择题和简答题里占30%-40%数据结构与算法直接对应编程题也占三成以上操作系统和网络虽然占比相对低但几乎每年都考属于拉分项。我的建议是复习顺序按“算法语言”优先操作系统和网络作为第二梯队整体效率最高。2. 选择题高频考点从C语法到网络基础2.1 C语法与语言特性考点选择题部分C语法是重头戏。常考的点其实很集中我列一下指针和引用的区别。几乎是必考题要能说清楚引用必须初始化、不存在空引用、引用更安全等核心差异。sizeof 和 strlen 的区别。考的是本质sizeof 是编译期运算符strlen 是运行时函数。虚函数机制。虚函数表、虚函数指针、析构函数为什么要加 virtual这些都是高频题。const 和 constexpr 的区别。C11 之后这俩经常被拉出来对比。static 关键字在不同上下文中的含义。复习这些知识点我建议不要光背结论。比如问到虚函数除了知道“有虚函数的类内部存在虚表指针”还要能推导出对象内存布局。笔试常考的一种题型是给你一个类让你写出创建对象后 sizeof 的结果。64位机器上一个虚函数表指针占8字节如果类里还有一个 int 成员那对齐后的结果是16字节。这种题考的就不只是概念而是你对内存布局的敏感度。2.2 内存与STL底层内存管理是C/C笔试的“灵魂”考点。new/delete与malloc/free的关系是必背题malloc只分配内存new先分配再调构造函数free只释放内存delete先调析构再释放。还有一点容易漏new[] 要对应 delete[]混用属于未定义行为。STL方面vector扩容机制几乎年年考。vector底层是一块连续内存容量不够时会重新申请一块更大的内存把旧元素拷贝或移动过去再释放旧内存。常见选择题是问 size 和 capacity 的区别或者问为什么频繁 push_back 性能差。编程题里如果没提前 reserve大量插入时也会拖慢程序。同样map 与 unordered_map 的底层差异也是经典题红黑树 vs 哈希表分别是 O(logn) 和 O(1) 平均复杂度适用场景完全不同。智能指针是C11之后的高频点。unique_ptr 独占所有权shared_ptr 用引用计数共享所有权weak_ptr 用来打破循环引用。选择题喜欢让你判断一个代码片段会不会崩、会不会内存泄漏这时候要能识别出典型错误场景用裸指针初始化 shared_ptr 后又手动 delete、循环引用导致计数永远不为0、自定义删除器不匹配等。2.3 操作系统与网络基础操作系统常考的点不多但很固定。进程和线程的区别是必背的进程是资源分配的最小单位线程是CPU调度的最小单位同进程的线程共享地址空间进程之间相互独立。死锁的四个必要条件也要滚瓜烂熟互斥、持有并等待、不可剥夺、循环等待。选择题经常给一个场景让你判断是否死锁一旦发现资源不满足“不可剥夺”就能立刻排除答案。锁的种类也值得梳理一遍互斥锁、读写锁、自旋锁、条件变量。高频考点是互斥锁和自旋锁的区别——互斥锁让线程睡眠适合临界区较长的场景自旋锁忙等待适合临界区极短的场景但会浪费CPU。网络这块TCP三次握手和四次挥手属于送分题但细节容易错。我建议把“为什么需要三次握手”和“TIME_WAIT状态出现在哪一端”都搞清楚。socket编程也是常客包括 listen、accept、recv 等函数的作用以及阻塞与非阻塞的区别。如果出现HTTP相关题目通常不会太深状态码和请求方法知道就行。这些内容看起来多每一块也就一两天的复习量优先掌握高频概念。2.4 Linux与编译调试常识Linux命令偶尔出现在选择题里最常见的有 grep、find、ps、top、netstat、chmod、tar。这类题一般不考复杂参数而是考“用什么命令能完成某个操作”。比如查找当前目录下包含某个关键字的文件应该用 grep -r而不是 find。编译方面gcc/g 的常用参数要知道-o 指定输出文件名-g 生成调试信息-Wall 开启警告-stdc11 指定语言标准。一个经典送分题是 gcc 和 g 的区别正确理解是 g 能编译C代码并在链接阶段自动加上C标准库。还有一点容易被忽视头文件的 include 路径使用 -I 可以添加搜索目录这在一些改错题里会遇到。调试这块GDB不一定会考但值得花半天把 break、next、print、bt 这几个基础命令过一遍。编程题调试时如果你在本地都跑不明白考场就更难处理。我自己的一个习惯是所有题目都在本地写好并测试通过后再粘贴到提交框靠平台反复提交来试错是最浪费时间的做法。3. 编程题实战三套代码模板覆盖高频题型3.1 线上笔试的输入输出处理编程题的第一步是输入输出很多人栽在格式上。线上笔试平台通常要求从标准输入读数据往标准输出写结果。几种最常见的场景先列出来单组输入直接cin n。多组输入到EOF用while (cin n)循环。读一行含空格的字符串用getline(cin, str)注意之前如果有 cin 操作要先调用cin.ignore()清掉换行符。输出精度控制用printf(%.2f)或cout fixed setprecision(2)。另外能直接用 scanf/printf 就尽量用。虽然经过优化后 cin/cout 很快但考场上的编译器版本和优化情况你无法确定稳妥起见用 scanf/printf 几乎没有风险。如果非要用 cin/cout记得在 main 开头加这两行std::ios::sync_with_stdio(false); std::cin.tie(nullptr);这两行的作用是把C流与C标准IO解绑再加上关闭与 stdio 的同步能让 cin/cout 速度接近 printf。线下测试时也许感觉不到差距但数据量一大这行的必要性就体现出来了。3.2 链表与指针类合并两个有序链表链表题是C/C面试笔试常客。以合并两个有序链表为例展示编程题的完整解题流程。题目描述大致是输入两个递增链表合并为一个递增链表并返回头节点。解题思路有两种递归和迭代。我推荐迭代逻辑直观不容易爆栈。struct ListNode { int val; ListNode *next; ListNode(int x) : val(x), next(nullptr) {} }; ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) { ListNode dummy(0); ListNode* cur dummy; while (l1 l2) { if (l1-val l2-val) { cur-next l1; l1 l1-next; } else { cur-next l2; l2 l2-next; } cur cur-next; } cur-next l1 ? l1 : l2; return dummy.next; }这道题有两个关键点。一是用 dummy 节点统一头节点的处理逻辑避免单独判断返回哪个节点二是最后把剩余链表直接接上时间复杂度 O(nm)空间 O(1)。笔试里这种题一定要在15分钟内写完并自己虚拟跑一遍边界l1 或 l2 为空、两个链表等长、一个链表特别长。你能把边界控制在恒定状态基本不会出错。3.3 字符串与滑动窗口最长无重复子串字符串处理在笔试编程题里出镜率很高。最长无重复子串是一道典型题既考滑动窗口思想又考哈希表的应用。题目描述给定一个字符串 s找出其中不含重复字符的最长子串长度。经典解法是滑动窗口加哈希表记录窗口内字符出现次数。#include string #include unordered_map #include algorithm int lengthOfLongestSubstring(const std::string s) { std::unordered_mapchar, int count; int left 0, ans 0; for (int right 0; right (int)s.size(); right) { count[s[right]]; while (count[s[right]] 1) { --count[s[left]]; left; } ans std::max(ans, right - left 1); } return ans; }这个解法的本质是固定右指针逐步收缩左指针直到窗口内没有重复字符。时间复杂度 O(n)空间 O(字符集大小)。笔试时常见的失误是收缩 left 时忘了把对应字符计数减掉导致后续判断错误。写完代码之后建议用 “abcabcbb” 这个用例手动走一遍能快速发现这类 bug。这类题考的不只算法还有对哈希表操作的熟练度。3.4 算法思维类二分答案模板二分查找本身是基础算法但笔试里更喜欢考“二分答案”这种变体。核心思路是题目描述里出现“最大值最小”“最小值最大”或者“求某个临界值”九成可以用二分答案解决。以“给定一个升序数组找出第一个大于等于 target 的位置”为例给出通用模板#include vector int lowerBound(const std::vectorint nums, int target) { int l 0, r (int)nums.size(); while (l r) { int mid l (r - l) / 2; if (nums[mid] target) { r mid; } else { l mid 1; } } return l; }注意模板里两个细节。mid 用l (r - l) / 2计算避免l r溢出区间是左闭右开r 初始化为 size() 而不是 size()-1这样返回的 l 天然能表示插入位置。笔试时二分答案往往套在一个判断函数外层内层用贪心或模拟判断某个值是否可行。只要把这个模板固定下来遇到类似题就当作换一个判断函数的功夫能节省大量思考时间。4. 笔试踩坑记录环境、边界、时间分配4.1 线上笔试环境与提交细节线上笔试平台有很多种牛客、赛码、以及企业自研平台规则差异不大但细节容易踩坑。提前要做的事有以下几件。提前确认平台支持哪些语言和编译器标准。有些平台默认 g 4.8根本不支持 C17 特性用了就编译失败。本地开发推荐 VSCode 加 MinGW-w64或者直接用 WSL 里的 g把编译调试流程彻底跑通。我第一次笔试时因为本地用 IDE 一键运行对命令行编译报错完全没概念结果提交后 WA 了都不知道怎么本地复现。注意内存限制和时间限制。C/C题目的时间限制通常是 1000ms 到 2000ms如果算法复杂度太高即使答案正确也会超时。提交之前一定要检查返回值main 函数末尾 return 0 不是可选项而是必须某些平台连头文件多了少了都会报编译错误。4.2 代码实现中的典型错误编程题实现环节我见过且自己也踩过的坑挑几个典型的说。数组越界是最常见的。写循环时少写一个等号或者使用 vector 时没检查 size 就取下标都会出问题。整数溢出也很隐蔽两个 int 相加超过 2^31-1直接爆负数。遇到求和、乘方相关先想清楚要不要用 long long。迭代器失效是另一个高频坑在遍历 vector 时做插入或删除操作会导致迭代器失效笔试里建议用下标配合反向遍历或者先标记后删除。递归爆栈也值得注意。递归写的 DFS 在数据量大时会爆栈改成显式栈或迭代更稳妥。多组输入没处理干净是很多人丢分的原因上一次循环留下的状态没有清空导致结果错乱。这些问题单独看都不难但在限时环境下会被无限放大。我的技巧是写代码之前先想好边界用例写完后立刻用最简单的样例验证再提交。宁可花两分钟验证也不要把第一版草稿直接交上去。4.3 时间分配与整体策略通用策略是先做选择题因为选择题分值固定且一些题可以通过排除法快速定位编程题从最简单的开始做先把能拿到的分数拿到手。我一般会给三道编程题分别设死 30、20、15 分钟的上限超过上限就跳过做下一道最后再回来补。还有一个容易被忽略的点读题。编程题题干通常包含大量描述有人反复读三遍才看明白非常浪费时间。我的做法是第一遍快速扫题面只关注输入输出格式和样例第二遍仔细读条件把数据量、时间限制标注出来第三遍才开始想算法。选择题部分如果一道题卡了超过3分钟直接标记后放弃别让单道题拖垮整场节奏。考场上没有“完美完成”一说能把会的题全做对已经是很好的结果。5. 备考节奏与资料取舍一个月内怎么准备5.1 考前四周的安排建议如果距离笔试还有一个月我的建议是按三个阶段走。第一周把C/C基础和操作系统、网络的高频知识点过一遍。不需要看大部头教材直接看知识点总结和经典题目。比如林锐的《高质量C/C编程指南》篇幅不大但很多笔试选择题的原型都来自里面提到的代码风格问题值得翻两遍。第二周集中刷数据结构和算法。剑指offer里的题尽量自己做一遍不需要全背但要把链表、树、栈队列、二分、动态规划这几类的解题套路吃透。LeetCode 精选的 top100 也可以刷重点是每道题都自己写到能 AC。第三周进入模拟笔试状态。每天固定时间做一套组合练习选择题加两道编程题严格按照考试时间执行。这一阶段的目标不是刷题量而是熟悉节奏和查漏补缺。最后几天回归基础别碰新题。把错题本翻一遍把常见坑再看一遍轻装上阵。5.2 资料推荐与取舍原则网上关于C/C学习的资料非常多但笔试准备时间有限要抓大放小。我自己用得比较多的是这几种林锐《高质量C/C编程指南》短小精悍覆盖大量容易犯错的语言细节。《剑指offer》面试题的经典集合很多笔试编程题就是从中变形而来。LeetCode 精选特别是数组、链表、字符串、二分、动态规划标签用来练手感和算法思维。牛客网的笔试真题库用来熟悉平台风格和常见题型。如果学有余力还可以找几套 GESP 等编程等级考试的题目来练练手这类题对算法和数据结构的考察很规范能帮你检查有没有知识盲区。不要买一堆大部头书准备笔试不是系统学习是工具化复习。把高频题型的解题模板练到肌肉记忆比看十本教材都有用。5.3 笔试之后的延伸准备笔试只是校招的第一关后面的面试大概率会围绕笔试内容展开追问。常见的问题方向包括项目里有没有做过多线程是怎么解决锁冲突的有没有遇到过内存泄漏怎么定位的TCP连接建立的过程能不能画出来等。这些本质上还是笔试那些知识点的延伸。我建议笔试结束后马上做一次复盘把没做出来的题、蒙对的题、想起来的每一道考点都记下来第二天重新做一遍。这个习惯能让你在面试前快速形成自己的知识体系。如果笔试里出现了你完全没见过的考点比如某个C标准库函数就说明你的知识盲区在那里趁面试前补上。应对笔试的方法其实就一句话知识地图铺开高频题型练熟剩下的交给心态。最后再分享一个小技巧准备一个自己的代码模板库把输入输出处理、二分模板、链表节点定义这类随时要用的代码片段存好。笔试开始后先在草稿纸上或本地把模板写好再开始做具体题目能省下不少时间。这个习惯我到现在写代码还在用效率提升很明显。