
2018年秋天我在北京参加了爱奇艺的校招笔试岗位是C工程师第二场。那场笔试给我的印象特别深不光是题量大更重要的是它把C/C语言基础、算法和工程能力全部装进一张卷子里。很多同学准备校招时只顾着刷 LeetCode把语言本身的细节放在一边结果一看到指针和内存管理的题就露怯。这篇文章我完整复盘一下那场笔试的考察方向、典型题目解法以及我后来在面试和实际工作中总结出来的备考方法。如果你现在正在准备C方向的校招或者想了解2018年这一轮爱奇艺校招的难度可以参考一下。1. 这场笔试到底在考什么全景复盘爱奇艺的C工程师岗位业务线覆盖了视频播放、音视频编解码、推荐系统、搜索、后端服务这些核心方向所以笔试不会只考一门语言而是把计算机基础课全部串起来考。我拿到的第二场卷子题量大概在40道左右考试时间90分钟题型分成三块选择题、填空题、编程题。选择题里C/C语言细节占了大头比如sizeof和strlen的区别、const的用法、指针数组和数组指针的辨析、static关键字在不同场景下的作用。操作系统和网络也有不少像进程线程区别、死锁条件、TCP四次挥手、socket编程相关的问题。数据结构算法部分选择题会结合复杂度分析来出比如给定一个循环问你时间复杂度是多少。编程题通常有两道到三道难度是递增的。我印象里有一道是字符串处理另一道是链表或者二叉树相关的题。这种题目难度不大但很考验边界处理能力比如空指针、空字符串、溢出这些情况如果你平时写代码没有养成考虑边界的习惯容易被扣分。填空题则偏向于让你写出某个程序的输出结果这类题非常需要耐心要一行一行模拟。整体来看这场笔试的定位不是“筛天才”而是筛掉基础不牢的人。爱奇艺要的是能立刻上手做业务的工程师不是只会背模板的刷题机器。所以备考时我建议你把重心放在“理解原理”而不是“背诵代码”上。2. 语言基础与内存管理C工程师的命门2.1 C与C的分寸感别把两份卷子答成一份很多同学把C和C混在一起学最后哪个都不精。爱奇艺的卷子很有意思它会故意不写清楚“请用C或C”让你自己选。这时候你要明白C工程师岗位要求你两者都能驾驭但你要清楚哪些是C的写法哪些是C的写法。比如“字符串逆序输出C语言”这种社区里常搜的热门题目如果手写C版本你通常用一个char数组然后双指针交换。如果切换到C可以用std::reverse也可以操作std::string。笔试现场我更推荐C写法因为标准库能帮你减少很多边缘情况的判断。但面试官之后可能会追问“你用的是C的库如果让你不用库纯C怎么实现”这说明单纯会调用API是不够的你得知道底层逻辑。C和C的区别里最常考的几点是C没有类和对象C有C的malloc/free和C的new/delete不能混用C的字符串是char数组C有std::stringC的结构体不能有成员函数C可以。这些看起来简单但选择题里只要换个包装很容易掉坑。2.2 指针、数组与字符串最容易拿分也最容易丢分的地方这一块几乎每场校招必考。爱奇艺第二场的选择题里出现了一道非常经典的题目“char str[] hello; char *p hello; 这两个定义有什么区别”答案是数组内容可以修改指针指向的是字符串常量内容不可修改。很多人平时没注意写代码用p[0]q编译不报错运行直接崩溃。我在准备时把字符串相关的题目集中做了一遍包括“字符串逆序输出c语言”的实现还有“c语言字符串函数”的整理。最基础的手写版本是这样的void reverse_string(char *s) { if (s NULL) return; int len strlen(s); int i 0, j len - 1; while (i j) { char tmp s[i]; s[i] s[j]; s[j] tmp; i; j--; } }看起来很简单但考场上有两个坑第一反转的是整个字符串还是把单词顺序反转比如“hello world”变成“world hello”如果题目要求单词级别反转你需要先反转整个字符串再逐个单词反转这一步很多人在紧张时容易漏掉。第二strlen返回的是size_t和int比较时如果左边是int j len - 1当len为0时j的初值变成-1循环条件不成立没问题但如果你写j strlen(s)而strlen返回无符号数那么j -1实际上会变成一个很大的正数循环就出问题了。这种细节就是笔试现场常见的失分点。指针方面“c语言指针”这个热搜词背后是大量题目比如“int *p[3]”和“int (*p)[3]”的区别指针函数和函数指针的区别二级指针什么时候用等。我的建议是在纸上画出内存布局这个习惯特别管用。你画一个方框代表变量再画箭头代表指针指向很多抽象问题立刻清晰了。2.3 内存管理的三板斧堆、栈、泄漏与野指针“c语言内存管理”也是爱奇艺笔试的常客。选择题里会问“下面代码有什么问题”char *get_str() { char str[] hello; return str; }问题在于str是栈上局部数组函数返回后内存已经失效调用方拿到的是一个悬垂指针。这就是经典的野指针来源。再看一个int *p (int *)malloc(sizeof(int) * 10); free(p); // 后续又使用pfree之后没有将p置为NULL后面再使用就是“野指针”。笔试中经常会考free之后指针本身的地址值没变但该内存已经归还给堆管理器访问它属于未定义行为。内存泄漏的题也很多常见的就是malloc后忘记free或者new后忘记delete。C里还喜欢考“构造函数中new了成员变量析构函数忘了delete”以及“new []和delete []必须配对使用”这个点。我在实际开发中用的排查工具是Valgrind笔试备考阶段我也建议你在Linux上跑一跑。它会把每一块泄漏的内存、分配位置和执行路径都打印出来比用眼睛看代码高效太多。Windows下可以用Visual Studio的CRT调试或Dr. Memory效果类似。2.4 文件读写操作别以为笔试不考I/O“c语言文件读写操作代码”的热度一直很高可能是因为很多课程设计都会涉及但校招笔试也偶尔会考尤其是填空题让你补全fopen、fread、fwrite的参数。爱奇艺的卷子里我记得有一道填空题“用C语言将一个二进制文件的内容复制到另一个文件补全代码。”核心代码如下FILE *src fopen(in.dat, rb); FILE *dst fopen(out.dat, wb); char buf[1024]; size_t n; while ((n fread(buf, 1, sizeof(buf), src)) 0) { fwrite(buf, 1, n, dst); } fclose(src); fclose(dst);这个题考察的点是文件要使用二进制模式“rb”和“wb”否则在Windows下会出现换行符被转换的问题fread的返回值是实际读到的字节数最后一次读取可能不足缓冲区大小所以要按返回的n来写。如果你直接用sizeof(buf)作为fwrite的长度会把上一次残留的数据也写进去造成文件损坏。C版本则多半考察ifstream和ofstream使用方法是cpp std::ifstream in(in.dat, std::ios::binary); std::ofstream out(out.dat, std::ios::binary); out in.rdbuf();这种写法比C简洁但面试官可能会追问底层实现。3. 算法题是重头戏从排序到字符串处理的实战拆解3.1 排序算法手写冒泡只是开胃菜热搜词里有“冒泡排序c语言”说明很多同学刚学算法时会从冒泡入手。爱奇艺第二场笔试的选择题考过冒泡排序的时间复杂度编程题则很少直接让你写冒泡。倒是有一次面试环节面试官让我现场写快速排序然后问我“快排最坏情况是什么如何避免”。手写快排是基本功建议你练到闭上眼睛也能写出来的程度void quick_sort(int arr[], int l, int r) { if (l r) return; int i l, j r, pivot arr[l (r - l) / 2]; while (i j) { while (arr[i] pivot) i; while (arr[j] pivot) j--; if (i j) { std::swap(arr[i], arr[j]); i; j--; } } quick_sort(arr, l, j); quick_sort(arr, i, r); }注意我用了pivot取中间值而不是取第一个元素这是为了避免在近乎有序的数组上退化成O(n^2)。如果你只知道“快排选第一个数”面对有序数组很容易挂。排序相关的题目里我建议你顺带准备归并排序、堆排序特别是归并排序的“合并两个有序数组”思想在多个面试题里都会用到。3.2 字符串处理逆序、回文、子串高频出现“字符串逆序输出c”这个热搜词背后的问题往往不会只让你反转一个字符串。高配版本是“反转字符串中的单词顺序”比如“I love coding”变成“coding love I”。这种题目的思路是先整体反转再对每个单词做反转。核心代码void reverse_word_order(std::string s) { std::reverse(s.begin(), s.end()); int start 0; int n s.size(); for (int i 0; i n; i) { if (i n || s[i] ) { std::reverse(s.begin() start, s.begin() i); start i 1; } } }做题时要特别小心多空格分隔的情况。有的题目要求保留多个空格有的要求压缩成单个空格审题一定要仔细。回文判断、最长回文子串也是爱奇艺这类公司笔试的高频题。最简单的是中心扩展法复杂度O(n^2)笔试时间紧时可以先写这种再和面试官讨论用Manacher优化到O(n)。3.3 二叉树与链表递归和非递归都要能写爱奇艺的编程题里链表和二叉树出现过不止一次。链表类题目常考“反转链表”和“判断链表是否有环”。反转链表的递归写法很优雅但笔试现场我更推荐迭代版因为不容易爆栈ListNode* reverse_list(ListNode* head) { ListNode *prev nullptr, *cur head; while (cur ! nullptr) { ListNode *next cur-next; cur-next prev; prev cur; cur next; } return prev; }二叉树题目里“求二叉树深度”是典型的递归题int max_depth(TreeNode* root) { if (root nullptr) return 0; return 1 std::max(max_depth(root-left), max_depth(root-right)); }但也可能考“层序遍历”那就需要队列常见的写法如下void level_order(TreeNode* root) { if (root nullptr) return; std::queueTreeNode* q; q.push(root); while (!q.empty()) { int size q.size(); for (int i 0; i size; i) { TreeNode *node q.front(); q.pop(); // 处理node if (node-left) q.push(node-left); if (node-right) q.push(node-right); } } }这种题不要求高深技巧但要求你边界处理干净、代码结构清晰。爱奇艺的在线笔试系统不会给你完整的编译器提示写完代码后心里一定要自己跑一遍测试用例。3.4 图论与最短路径迪杰斯特拉偶尔会出现热搜词里的“迪杰斯特拉c”说明不少同学在准备图算法。虽然爱奇艺第二场笔试没有直接考Dijkstra但我在其他公司的校招里遇到过。Dijkstra适合单源正权最短路朴素实现O(V^2)堆优化O((VE)logV)。我建议你准备堆优化版本因为面试官看你写朴素版很可能会追问“数据量大时怎么办”。void dijkstra(const vectorvectorpairint,int graph, int src, vectorint dist) { int n graph.size(); dist.assign(n, INT_MAX); dist[src] 0; priority_queuepairint,int, vectorpairint,int, greater pq; pq.push({0, src}); while (!pq.empty()) { auto [d, u] pq.top(); pq.pop(); if (d ! dist[u]) continue; for (auto [v, w] : graph[u]) { if (dist[v] dist[u] w) { dist[v] dist[u] w; pq.push({dist[v], v}); } } } }图论题在视频网站的后端服务里很常见比如分发网络中的节点选择、推荐关系图里的路径计算。笔试考到的时候虽然不多但了解原理会让你在面试聊项目时更有底气。4. 一套可复现的笔试自测题像“第二场”一样练手感4.1 自测题单与评分标准我不能保证爱奇艺还会出同样的题但你可以用下面这套题模拟第二场的节奏。每道题控制在15分钟以内总分100分建议得分80分以上再去投递。题号题目考察点建议用时1给定字符串去除首尾空格并将中间连续多个空格合并为一个数组操作、双指针10分钟2实现一个单链表反转要求用迭代和递归两种方式链表、指针操作15分钟3找出数组中出现次数超过一半的数字摩尔投票法 / 哈希统计10分钟4设计一个内存池支持分配和释放固定大小内存块内存管理、链表20分钟第1题考的是日常字符串处理能力别小看它视频网站的后端日志清洗里全是这类需求。第2题是链表基本功递归写法要能说清递归返回的逻辑。第3题是经典算法哈希统计最简单摩尔投票法更高效面试官会喜欢后者。第4题是加分项如果你能写出一个简单的空闲链表内存池说明你对内存管理有真实理解。4.2 考场时间分配与答题顺序我的经验是先把所有题目扫一遍优先做编程题里你最有把握的那道。因为编程题分值高一旦卡住容易导致后面会做的选择题都没时间写。选择题遇到不确定的先标记别恋战时间不够时也要凭第一感觉选一个爱奇艺的笔试通常不扣分所以空着是最亏的。具体时间分配上我的习惯是选择题和填空题一共控制在35分钟内剩下55分钟全部留给编程题。如果你25分钟还没搞定第一道编程题我会果断放弃跳到下一道。笔试不是追求满分而是追求总分最大化这个思路很重要。4.3 交卷前的自查清单交卷前我会留5分钟做这几件事检查所有变量是否初始化尤其是计数器、指针、数组下标。检查数组越界循环到n-1还是nstrlen之后有没有把结尾的\0算进去检查指针是否为空尤其是在函数入口和malloc之后。检查是否有动态分配的内存没有释放。检查代码是否能正确编译有没有漏掉头文件、分号、大括号。这几条看起来基础但能挽回不少分数。我见过太多人整个函数写得很漂亮就是忘记头文件或写错了返回类型导致编译不过整个题零分。在线笔试的编译器通常不会给你逐行调试的机会所以平时就要养成“写代码如同写草稿”的习惯。5. 面试环节的进阶准备笔试之外还要搞定什么笔试通过之后紧接着就是面试。爱奇艺的面试一般有两到三轮技术面加一轮HR面技术面里会针对笔试内容进行深入提问。比如你笔试里写了字符串反转面试官可能会问“如果这个字符串非常大内存放不下怎么办”这就涉及外部排序或分块的思路你可以回答“用双指针配合文件读写分段处理”。C岗面试必问的一项就是智能指针。爱奇艺的面试官喜欢让你对比unique_ptr、shared_ptr、weak_ptr再让你说说shared_ptr的引用计数线程安全性。这些虽然不算2018年第二场笔试的题目但你拿到面试之后一定要准备。我的建议是用最朴素的话记住unique_ptr独占所有权shared_ptr共享所有权weak_ptr不增加引用计数用来打破循环引用。然后配合一个带循环引用的结构体例子讲一遍。多线程也是爱奇艺面试的高频点。视频转码、推流、弹幕分发这些业务天然依赖并发。常见题目有std::thread怎么用mutex和condition_variable搭配时要注意什么有没有用过原子变量以及经典的生产者消费者模型要求现场手写。建议你提前准备好一个简单的线程池实现这非常加分。另外爱奇艺的业务是视频所以网络协议相关的问题也经常出现。比如TCP和UDP的区别、HTTP和HTTPS的区别、视频直播用的是RTMP还是HLS、CDN是怎么做内容分发的。我建议你在准备技术栈时顺便了解一下视频点播里的“首帧时间”“卡顿率”这些指标面试时聊起来会显得你懂业务。项目经验方面如果你的简历里写了C项目面试官一定会深挖。我被问到最多的三个问题是项目中最复杂的一个模块是什么遇到内存泄漏或崩溃时怎么排查做过哪些性能优化这些都要提前准备不要临时编。真实项目里哪怕是很小的优化比如用move语义减少拷贝、用reserve预分配vector容量都可以讲清楚细节。6. 备考实操中的常见问题与排查技巧实录6.1 为什么平时练习能写对一到笔试就崩这个现象太常见了。原因多半是平时练习时依赖IDE的自动补全和编译报错提示一旦到了在线笔试环境编译器变得“不友好”错误信息也不明显心态就慌了。我的对策是从备考第一天起就在一个尽量接近笔试的环境里练习比如用命令行直接编译运行关掉语法高亮和自动补全。这样写出来的代码才经得起考场环境考验。6.2 在线编辑器里的“编译错误”怎么快速定位常见的在线编辑器不会告诉你具体哪一行报错只会给出一段模糊的编译日志。我的排查顺序是先看是否缺少头文件比如用了strlen没加string.h用了std::sort没加algorithm。检查函数返回类型是否所有分支都有return。检查括号和分号。检查数组越界但一般编译器不会报这个运行时会崩溃。如果题目给了示例输入输出一定要把示例跑通再提交。没有给的话自己构造几个简单用例空输入、单元素输入、最大规模输入。6.3 字符串和数组越界最隐蔽的“运行时崩溃”我备考时专门整理过一个错误案例vectorint nums {1, 2, 3}; for (int i 0; i nums.size(); i) { cout nums[i] endl; }这段代码的bug是i nums.size()当i等于3时访问nums[3]越界。但由于vector的内存布局这个越界访问可能不会立刻崩溃而是一段时间后才出问题特别难排查。笔试时遇到这种题目要直接能看出问题所在。解决办法是养成使用size_t或int i 0; i (int)nums.size()的习惯。但要注意如果你用size_ti--到0之后再--会变成无符号最大值导致死循环。所以循环里要统一类型。6.4 内存泄漏工具不会看怎么办很多同学觉得Valgrind用起来复杂实际上只需要记住一条命令valgrind --leak-checkfull ./your_program它会输出“definitely lost”的字节数并告诉你泄漏发生在哪个函数。笔试备考阶段我会专门写几个带泄漏的小程序然后用Valgrind跑观察输出日志这样之后写代码时就会对malloc/free的配对格外敏感。6.5 笔试前一天应该做什么我不建议笔试前一天还刷难题更不建议熬夜。正确做法是快速翻一遍自己整理的笔记把常见的API语法扫一遍比如map、set、unordered_map的find用法string的substr、find、erase用法然后早点睡觉。考试当天带上一支笔和一张白纸虽然在线笔试不允许用纸笔但遇到复杂逻辑时在纸上画内存图或测试用例能极大减少低级错误。从我个人的备考经验看爱奇艺2018秋季校招C工程师第二场这场笔试难度并不算高但覆盖面很广它考的不是某个冷门算法而是你是不是一个能写出“干净代码”的人。C/C语言基础、内存管理、算法基本功、周边计算机网络知识这几个方向按优先级准备你的通过率会高很多。最后分享一个小技巧每次做完一道题别急着看答案先自己构造三个测试用例一个正常、一个边界、一个异常跑通后再对照答案这比做十道题都有效。