尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

浩鲸科技C类笔试备考:C/C++核心考点与实战策略

浩鲸科技C类笔试备考:C/C++核心考点与实战策略 “浩鲸科技2019校招C类笔试题”这个标题我在几个校招群里见过不止一次。那年秋天很多同学把它当成“阿里系”里最跳一跳能够到的目标——浩鲸科技背靠阿里云和中兴通讯的基因做的是电信运营商核心的BSS/OSS系统这类公司笔试筛人的逻辑和纯互联网大厂不太一样它更看重你用C/C去思考底层问题的能力。这篇文章不打算给你押题因为任何一家公司的笔试每年都在变背题是性价比最低的备考方式。我想做的是把这份笔试题背后的知识体系拆开讲清楚“C类笔试到底考什么、为什么考、怎么准备最划算”让你面对的不再是一张陌生的试卷而是一套可以复用到同类企业华为、中兴、海康、讯飞这些的备考方法论。1. 先搞清楚浩鲸是谁才能看懂它为什么出这些题1.1 从通信软件到数字化服务双基因决定了考点浩鲸科技的前身是中兴软创2018年前后完成重组成为阿里巴巴和中兴通讯共同滋养的数字化服务厂商。注意这个“双重基因”一方面继承中兴做电信设备与运营商服务时的严谨工程文化另一方面又向阿里靠拢学互联网的迭代速度和海量并发处理思路。这两股力量叠加决定了它的校招笔试既不会像纯互联网大厂那样只考偏门算法也不会像传统硬件厂商那样追问寄存器细节而是卡在“用C/C做大规模系统软件”这个位置上。它最核心的业务是电信运营商的BSS/OSS——计费、客户关系管理、业务开通、网络资源调度这些后台系统。这类系统的特点是数据量以亿计、并发峰值极高、代码生命周期长达十数年所以笔试要筛的人必须真正理解内存、理解对象生命周期、理解系统调用背后的代价。很多同学拿到卷子觉得“怎么考得这么基础”其实恰恰是这些基础决定了你能不能在生产环境里活过三年。1.2 “C类”到底指什么我当年备考时也纠结过这个问题。“C类”是C语言类别吗结合浩鲸的岗位发布习惯来看这里通常指C/C方向的技术笔试与JAVA类、前端类、测试类并列为不同类型的考卷。也就是说“C”是编程语言方向标签而不是试卷难度等级。从题型上看这份笔试试卷大体由三部分组成语言基础C/C语法、内存模型、面向对象、数据结构与算法手写代码题、计算机基础常识操作系统、网络、软件工程。下面我按性价比和踩坑程度把这三块的备考思路一个个拆开讲。2. C/C语言基础笔试里分值密度最高的部分2.1 指针、引用和const每次笔试的老朋友C/C类笔试卷子里指针和引用几乎是必考而且通常不是单独问概念而是通过代码片段让你判断输出、找错误、改bug。先说指针和引用的核心区别。引用是变量的别名声明时必须初始化一旦绑定就不能再换绑指针保存的是地址可以为空也可以随时指向其他对象。笔试里经典的考法是写一个swap函数让你判断传值、传引用、传指针三种写法哪个能真正交换传值不行传指针和传引用都行但传指针要处理空指针传引用更直接。更值得警惕的是const与指针结合的位置问题。const int* p表示p指向的内容不可修改但p本身可以改int* const p表示p本身不能改但它指向的内容可以改const int* const p则两者都不能改。这类题几乎每年都能看到只要画一张指针和所指向内存的图彻底理清“哪个是只读”就不会再丢分。还有两个高频陷阱。第一个是sizeof对数组名取sizeof得到的是整个数组的字节数但当数组作为函数参数传递时会退化为指针此时sizeof得到的是指针大小通常是8字节64位系统。第二个是字符串字面量char* str hello和char str[] hello截然不同前者指向只读常量区修改它会导致未定义行为后者是栈上的可写副本。笔试里那些让程序崩溃的题目很多就是这个原因。2.2 对象生命周期析构、拷贝和移动的精髓语言基础部分如果只复习一块我建议优先攻对象的拷贝控制。浩鲸这类做大型系统的公司特别看重这一点因为系统里的资源泄漏和崩溃十有八九都出在错误的对象生命周期管理上。最经典的考题是手写一个String类实现构造函数、析构函数、拷贝构造函数和拷贝赋值运算符。这四件套能综合考察深拷贝与浅拷贝、资源释放、自赋值处理和异常安全。class String { public: String(const char* str nullptr); String(const String other); String operator(const String other); ~String(); private: char* m_data; }; String::String(const char* str) { if (str nullptr) { m_data new char[1]; *m_data \0; } else { m_data new char[strlen(str) 1]; strcpy(m_data, str); } } String::String(const String other) { m_data new char[strlen(other.m_data) 1]; strcpy(m_data, other.m_data); } String String::operator(const String other) { if (this other) { return *this; } delete[] m_data; m_data new char[strlen(other.m_data) 1]; strcpy(m_data, other.m_data); return *this; } String::~String() { delete[] m_data; }注意几个细节默认析构函数不会帮你释放堆内存默认拷贝构造函数是浅拷贝两个对象会指向同一块内存析构时double free赋值运算符必须先判断自赋值否则先delete再拷贝会崩。如果你还能补一句“用拷贝交换或右值引用实现移动语义更优雅”那这题的印象分基本拿满。C11之后移动构造函数和std::move也开始出现在笔试里。核心是理解“把析构成本很高的资源转移出去而不是复制一份”。很多公司会追问unique_ptr和shared_ptr的区别以及哪些场景不能用shared_ptr比如循环引用要配合weak_ptr。这些都不会超纲但如果你只看语法不写代码很可能在“为什么这样做”上卡壳。2.3 static、volatile和内存布局细节里的魔鬼这类题目讲究“一眼识别陷阱”。static是出镜率极高的关键字static局部变量放在全局数据区生命周期到程序结束但作用域仍在函数内static全局变量和static函数都有内部链接属性只在当前编译单元可见类的static成员属于所有对象共享。volatile则常和编译器优化一起考告诉编译器该变量的值可能在当前线程之外被改变不要优化掉对它访问。笔试可能会给一个看似无限循环的程序问为什么用volatile修饰后行为不同。内存布局同样值得花时间。经典问题是进程地址空间的分区代码段、未初始化数据段BSS、已初始化数据段、堆和栈。堆从低地址向上增长栈从高地址向下增长这两个方向常被考到。结构体对齐和大小计算更是老熟人记住“每个成员按自身对齐值对齐整体按最大对齐值补齐”的基本规则后多数题都能算。再加上new/delete和malloc/free的区别前者会调用构造/析构函数后者只分配/释放内存new的底层是malloc配对使用别混搭这几块组合起来就是语言基础里最常见的失分点。我是建议准备一张大表把“指针与数组”“const的多种位置”“栈与堆的区别”“static的四种作用”列清楚考前过一遍比看十篇面经都有用。3. 数据结构与算法绝大多数人的分数分水岭3.1 高频数据结构与算法其实就那几类很多同学一听到算法就怕觉得要刷几百道题才敢上场。但校招笔试和ACM竞赛是两回事它考的是工程实用向的算法频率极高的题型其实非常集中。类型典型题目方向优先级链表反转、找环、合并有序链表、删除节点极高二叉树前/中/后序遍历、层次遍历、最大深度、最近公共祖先极高字符串最长无重复子串、回文子串、字符串转整数、KMP高排序/查找快排、归并、堆排、二分查找边界高动态规划背包、最长递增子序列、编辑距离中高栈/队列单调栈、用栈实现队列、最小栈中哈希表两数之和、重复元素判断、缓存设计中链表和二叉树是手写代码题的重灾区因为它们的遍历、递归和指针操作最能体现基本功。复习时不要只看思路每一类都要手写一遍尤其是非递归的二叉树遍历和链表反转很多同学到考场上一紧张递归的写法都会卡。排序算法里快排的partition写法和归并排序的merge写法要练到肌肉记忆。二分查找更是要小心边界left和right的更新条件、while里是还是、mid是向上还是向下取整这些细节在笔试里都是分。动态规划不需要钻太深但“最长公共子序列”“最长递增子序列”这类经典题最好能写出转移方程和边界初始化。3.2 手写代码题怎么被评分三个隐藏维度不少人以为手写代码题只要跑通测试用例就给满分这是个误解。笔试的代码题虽然不像面试手撕那样全程围观但评分时通常有三个维度。第一是正确性包含边界条件处理。比如输入空指针、空数组、只有一个元素的情况很多考生在正常用例上写得不错边界一上来就崩。一个链表的反转函数如果不处理空链表或只有一个节点的链表实际运行就会出问题。第二是复杂度。题目如果明确要求O(n)你却写了O(n^2)哪怕测试用例过了也会被扣分。这就要求你写代码前先想清楚时间复杂度和空间复杂度并在注释里写出来。很多公司会看这一点。第三是代码风格。命名是否符合规范、逻辑是否清晰、有没有写注释、有没有明显的冗余这些在代码评审里都算分。面试官看笔试代码几乎等同于看新人的提交代码一个缩进混乱、常量不提取、变量名是a/b/c的答案即使正确也很难拿高分。3.3 一个典型的编程题思路示例拿一道非常经典、同时也非常高频的题目来说步骤“给定一个字符串找出其中不含重复字符的最长子串的长度。”拿到题先别动手。第一步确认输入和输出第二步想清楚算法。最直观的暴力法是O(n^2)不推荐。正确做法是滑动窗口一个left指针指向窗口左边界一个right指针向右扩展用哈希表记录每个字符最近出现的位置。如果发现当前字符已经在窗口中出现过就把left跳到上一个位置1时刻更新最大长度。#include string #include vector #include algorithm using namespace std; int lengthOfLongestSubstring(string s) { vectorint lastPos(128, -1); int left 0, maxLen 0; for (int right 0; right s.size(); right) { char c s[right]; if (lastPos[c] left) { left lastPos[c] 1; } lastPos[c] right; maxLen max(maxLen, right - left 1); } return maxLen; }这段代码里我刻意加了向量索引方式而不是用map原因是字符范围固定时数组更快这也是面试官可能追问的点。你可以发现手写代码题的重点不在于你能背多少模板而在于你能不能把一个场景问题翻译成清晰的数据结构和稳定的循环不变式。4. 操作系统、网络与工程常识用碎片时间补性价比最高4.1 进程、线程与死锁选择题的常客计算机基础这部分通常以选择题和简答题出现并不要求你背操作系统课本但高频概念必须能说得清楚。进程和线程的区别是必考关键是那句话“进程是资源分配的基本单位线程是CPU调度的基本单位。”进程间通信方式要能列举出管道、消息队列、共享内存、信号量、信号、socket最好能说清各自适用场景比如共享内存最快但要自己处理同步管道适合父子进程之间通信。线程同步的互斥锁、条件变量、信号量、读写锁也是常客特别是“互斥锁和信号量在实际使用中怎么选”这种工程向追问。死锁几乎是每次笔试都会出现的概念题。四个必要条件互斥、持有并等待、不可剥夺、循环等待要能一字不差写出来并知道破坏任何一个条件就能预防死锁。如果题目给一个资源分配图让你判断是否死锁核心就是找循环等待链。虚拟内存部分要理解页式管理、页表、缺页中断、页面置换算法LRU、FIFO、Clock。笔试里考得最多的是LRU甚至会让你手写一个LRU缓存的get和put操作这题结合了哈希表和双向链表值得提前练一下。用户态和内核态的切换、系统调用的代价则时常作为“为什么频繁系统调用会慢”的引子出现。4.2 网络协议只需要掌握到这层网络部分的常考点集中在TCP/UDP和HTTP。TCP三次握手和四次挥手是必须的要能画出来更要能解释“为什么握手是三次不是两次或者四次”——本质上是为了防止失效的连接请求突然到达服务端导致服务端打开无效连接。如果题目继续追问TIME_WAIT状态要能说出它的两个作用保证最后的ACK能可靠到达对方以及让旧连接的所有报文在网络中完全消失。TCP的可靠传输机制里确认应答、超时重传、滑动窗口、拥塞控制慢开始、拥塞避免、快速重传、快速恢复是高频考点。笔试很少让你写拥塞窗口的完整变化但给一个场景判断cwnd上升或下降经常会出现。HTTP部分要能区分HTTP和HTTPS知道HTTPS的TLS握手大致流程以及常见的状态码200、301/302、403、404、500、502、503。对做C/C后台的同学来说还可能问到如何解析HTTP报文、Https通信如何建立、TCP粘包和拆包怎么处理。这块建议配合网络编程一起练既补笔试知识点也为后续面试做准备。4.3 软件工程题的“标准答案”思维这部分分数不高但很好拿因为考来考去就是面向对象原则、设计模式和工程规范。面向对象的基本特征是多态、继承、封装笔试里常给一段继承代码问你输出顺序。比如“基类构造、派生类构造、基类析构、派生类析构”的调用顺序以及虚析构函数存在的必要性通过基类指针delete派生类对象时如果析构函数不是virtual则派生类析构函数不会被调用导致资源泄漏。多态的实现原理——虚表和虚指针——也常考理解“每个包含虚函数的类有一个虚表对象里存储虚表指针”基本够了。设计模式考得不多但单例模式几乎是必背的。要会写线程安全的双重检查锁定版本并解释为什么用双重检查同时避免每次加锁的性能损耗和指令重排问题。另外工厂模式、观察者模式、策略模式也常以“让你说出使用场景”的方式出现。工程规范类的考点比较零散但有一年我印象很深的是简答题里让描述git的常用命令和协作流程。这说明浩鲸这类公司很看重新人能不能直接融入团队开发git add、commit、push、merge、rebase这些命令最好提前过一遍知道什么时候该用rebase什么时候该用merge算是基础工程素养。5. 实战做题节奏笔试现场最核心的博弈5.1 发卷后前10分钟先把全局扫描一遍笔试的时间普遍是90分钟到120分钟题量却不小。我见过很多同学拿到卷子就从头开始做结果在前面一道难题上耗了半小时后面三道会做的题没时间写。这是一个非常亏的策略。正确的做法是发卷后花3到5分钟把所有题目浏览一遍给每道题打一个“难度标签”一眼会做的、需要想一想但有思路的、完全不会的。然后按“先易后难、先拿分后攻坚”的顺序做题。选择题里有些概念题30秒就能出答案属于白送的分数务必优先拿代码题里第一道往往比第二道简单先做简单的练手再冲难的。我当年备考时养成了一个习惯定时模拟。每次都按考试倒计时来做一份真题或模拟卷做完只复盘两件事——每道题的实际耗时和哪类题最容易卡壳。这样真的上考场时时间感会好很多不会在不知不觉中陷进一道题。5.2 编程题的时间预算与完成顺序编程题通常是2到3道总分占比最大。按我的经验合理的做法是给每道编程题定一个硬性时间预算最多不超过25到30分钟超出就立刻切换到下一道。写编程题时先不要着急敲键盘。我推荐的顺序是先用注释或伪代码把整体思路写出来这既能让你的思路清晰万一时间不足评分者也能从注释里看到你的解题方向然后写关键的数据结构定义和主函数逻辑最后才补细节。一个很实用的技巧是先处理边界情况再写主体逻辑——空输入、只有一个元素、最大值、最小值先把这些if分支写出来再填中间的正常流程。完成代码后如果还有时间一定要在脑中或草稿纸上跑一遍测试用例尤其跑一个正常用例加一个边界用例。很多编译不过或者运行崩溃的问题在这个阶段就能被发现。笔试环境往往没有调试器所以提前在代码周围写清楚“输入是什么、输出是什么、复杂度多少”既是给自己留后路也是给阅卷人留好印象。5.3 如何给自己“留后路”写注释、拆子函数笔试代码是在有限时间内写的不可能像工程项目一样完美但有几个习惯能显著提高得分率。第一把复杂逻辑拆成小函数。比如一个链表题里需要反转链表单独抽一个reverseList函数既降低主函数复杂度也让阅卷人一眼看出你的模块化意识。第二关键步骤写中文注释。第三种不依赖IDE的自动补全命名尽量用完整单词比如maxLength而不是ml因为阅卷人会看代码风格简洁清晰的代码更容易拿高分。我也见过不少同学把时间花在把代码压缩得很短上为了少写几个字符让变量名变成单字母这在笔试中得不偿失。你要展示的是“能写出可维护代码”的潜力而不是代码高尔夫。6. 过来人的坑我的分数曾在哪些地方打折扣6.1 眼高手低只看题不写代码这是最普遍的一个坑。很多同学刷题时习惯看题解看一眼觉得“哦原来是滑动窗口”然后划走以为自己会了。到了笔试现场却在变量边界、循环条件上反复卡壳。我的建议是备考期间尽量用纯文本编辑器或白纸关掉IDE的自动补全手写代码。写完再对照标准答案重点看边界条件的处理和你漏掉的情况。链表题、二叉树题尤其适合在纸上画图推演一个指针的指向画错了代码自然就错了。你在纸上越熟练考场上的心态越稳。6.2 死磕一道题等于放弃整张卷笔试现场和面试最大的不同是面试官会引导你笔试不会。一旦你在一道题上耗太久整张卷子的节奏就可能崩掉。我见过一个真实的例子有同学在试卷的选择题环节遇到一道结构体对齐的题为了算清楚占用大小在那里推了七八分钟最后编程题只写了一个函数签名。这不是能力问题是策略问题。一道题分值再高也是有上限的把时间留给后面两三道能做对的题总体得分显然更高。如果你发现自己在一道题上超过15分钟还没有进展立刻标记一下先做后面的回过头来再看往往会有新思路。6.3 编码习惯的隐性扣分别不信最后一个想提醒的是编码习惯。笔试的编程题在交付后绝大多数公司会人工或半自动地查看代码一个命名规范、结构清晰、注释恰当的答案即使存在小瑕疵评分也往往优于一个功能正确但代码一团糟的答案。我自己在模拟复盘中就吃过亏写出的代码能跑但没有检查指针是否释放、没有处理空串、没有考虑入参合法性。后来我给自己列了一个“交卷前清单”变量是否初始化、动态内存是否释放、main函数是否返回0、有没有多余的debug输出、是否有注释说明思路。每次交卷前按清单过一遍能挽回不少因为粗心丢掉的分数。浩鲸19年那批笔试题的信息我已经记得不太全了但C/C方向校招笔试考核的底层逻辑这些年几乎没有变过。如果你也正在准备一场类似的技术笔试先从语言基础的地基打起把数据结构与算法的高频题练熟再补一点系统和网络常识最后用一场零失误的做题节奏去考场里检验自己。这样做你去哪家做C/C业务的公司都不会慌。
返回列表