C/C++校招笔试考点全解析:从指针内存到字符串算法
发布时间:2026/8/30 8:40:12 作者:尧图编辑部 阅读量:1,286

爱奇艺2018秋季校招的C/C工程师第二场笔试到现在还有人翻出来问。说实话这一场题目在当年算是有代表性的——难度比第一场略高出题方向更偏向工程细节不是说你把《C Primer》翻一遍就能稳过的。我当年刷完这套题复盘了很久后来又把同类型的题目整理成了一个知识清单面试阶段也用得上。这篇文章就把我整理的这些内容拆开讲不光是题目本身更重要的是背后的考点逻辑和备考方法希望能给正在准备校招的应届生、或者想回头补一补C/C底子的工程师一些参考。1. 先把笔试题型摸透这场考试到底在考什么1.1 考点分布三个维度撑起一场笔试爱奇艺这种体量的互联网公司校招笔试很少会只考单一知识点。通常一套卷子会覆盖三个维度语言基础、数据结构与算法、综合工程素养。第二场的题目在三个维度的分配上大概是4:4:2。语言基础考的是你对C/C这门语言的掌握深度不是“会用”而是“知道边界在哪”算法部分主要看代码能力能不能在限定时间内写出干净、正确、高效的代码综合工程素养则包括调试能力、内存管理意识、代码规范这类题目往往以改错或问答形式出现。为什么这么设计因为视频网站的后台服务大量涉及音视频处理、流媒体调度、CDN节点管理、大数据统计这些场景对C/C工程师的要求不是“能写业务代码”就行而是要能处理高并发、控制内存、优化性能。所以笔试题会偏向内存、指针、字符串、算法优化这些方向本质是在筛“能直接上手的人”。1.2 做题顺序与时间管理先把稳的分拿到手我见过太多人在笔试里倒在没有策略上。第二场卷子如果没记错选择题编程题附加题的总量在两个小时左右是偏紧的所以做题顺序直接决定你是否能写完。我的建议是第一遍先扫所有题目把选择题里一眼能确定的答案做掉不确定的做个记号不纠缠。第二遍做编程题从字符串、排序这类基础题开始先把通过率高的题解出来保证ACAccepted数量。第三遍回头看之前做记号的选择题最后再攻附加题或难度较高的算法题。这个顺序的核心是校招笔试不是竞赛目标是在有限时间内拿尽可能多的分数。一道选择题空着不亏分但编程题没通过就是几十分没了差距非常大。后面章节我会把每个部分的准备方式展开细讲。2. C/C语言基础笔试的“生死线”2.1 指针与内存管理最容易暴露水平的部分C/C笔试中指针和内存管理几乎是必考中的必考。第二场也不例外。这里考的不是“指针是什么”而是指针在不同写法下的行为差异、内存的生命周期、以及典型陷阱。有一个非常高频的考点malloc/free与new/delete的区别。如果你只是回答“一个是C的一个是C的”那这道题基本白给了。完整的回答要包含四点malloc只分配内存不调用构造函数free只释放内存不调用析构函数new/delete会调用构造和析构函数malloc返回void*需要强转new返回具体类型的指针malloc失败返回NULLnew失败抛出bad_alloc异常。更深入一点还要知道new[]和delete[]要配对使用不然可能导致析构次数不对或者内存释放越界。另一个经典题是“野指针和悬空指针的区别”。野指针是定义了但没有初始化的指针指向的地址是随机的悬空指针是指针指向的内存已经被释放但指针本身的值没被置空。实际项目里最危险的是悬空指针因为它在某些时候看起来“还能用”等你发现的时候数据已经错乱了。解决方法是释放内存后将指针置为NULL然后再用之前判断是否为空。再多说一点内存布局的问题。笔试里有一类题是让你分析代码里的内存分配与释放是否正确比如这样的场景#include stdio.h #include stdlib.h #include string.h char* get_string() { char buf[64]; strcpy(buf, hello); return buf; // 返回了栈内存地址函数结束后buf已失效 } int main() { char* p get_string(); printf(%s\n, p); // 未定义行为 return 0; }这段代码的问题在于get_string里返回的是局部数组buf的首地址而buf是栈上分配的函数返回后这块内存就已经被系统回收了。虽然打印时可能碰巧还能输出“hello”但这是未定义行为任何时候都可能崩掉。正确答案应该是用静态变量、堆内存或者由调用者传入缓冲区。这类题目表面考的是“这代码对不对”实际考的是你是否清楚栈、堆、静态区的生命周期差异。我把这个作为基础中的基础建议备考时闭眼都能画出来局部变量在栈、malloc/new在堆、全局和static在静态存储区、字符串字面量在只读常量区。2.2 字符串处理从函数实现到安全边界字符串在C/C笔试里出现的频率高到离谱。第二场也不例外既有直接的“字符串逆序输出”也有考察字符串函数底层原理的题。C语言里字符串就是字符数组以\0结尾这个特性决定了它所有操作的边界都必须由程序员自己小心控制。先说字符串逆序。这道题看起来简单但考察点是“你会不会用指针操作数组”。标准的原地逆序写法是用两个指针一个指向开头一个指向末尾交换字符并向中间靠拢#include stdio.h #include string.h void reverse_string(char* str) { if (str NULL) return; char* left str; char* right str strlen(str) - 1; while (left right) { char temp *left; *left *right; *right temp; left; right--; } } int main() { char s[] hello; reverse_string(s); printf(%s\n, s); // olleh return 0; }这里有一个容易被忽略的坑strlen计算长度时不包括结尾的\0所以在初始化right指针时要减1。如果你习惯用strlen(s)-1要确保字符串非空如果字符串长度为0strlen返回0right会变成指向str前面一个位置这是未定义行为。写防御性代码先判空再操作。再比如手写strcpy。两个版本一个是不安全的经典版一个是安全的带长度版本笔试让你对比哪个好char* my_strcpy(char* dest, const char* src) { if (dest NULL || src NULL) return NULL; char* ret dest; while ((*dest *src) ! \0); return ret; } char* my_strncpy(char* dest, const char* src, size_t n) { if (dest NULL || src NULL) return NULL; size_t i 0; for (; i n src[i] ! \0; i) { dest[i] src[i]; } for (; i n; i) { dest[i] \0; } return dest; }很多人在笔试时只写得出strcpy却忽略了返回目标地址这个要求以及目标缓冲区空间不足的溢出风险。strncpy的额外细节是如果src长度小于nstrncpy会用\0填充剩余空间如果src长度大于等于n不会自动补\0。所以正确使用strncpy要手动在dest[n-1]处赋\0这也是笔试里非常喜欢挖的坑。字符串相关的常见笔试还有查找子串朴素匹配和KMP的对比、字符串转整数考虑正负号和溢出、去除字符串中的空格、判断回文字符串、统计字符频率等。准备这些题时建议都把手写实现练一遍而不是只刷选择题。因为编程题很可能就是这些基础题的变体现场写代码的速度和准确度只能靠平时积累。2.3 关键字与语法细节while/do-while、const、staticC/C的关键字考察是选择题和判断题的重灾区。很多人觉得简单但其实细节很多。像热搜词里提到的“c语言while和do-while区别”就是典型的基础题但很多面试者回答得不够完整。while和do-while的核心区别是循环体至少执行一次还是零次。do-while先执行后判断所以循环体至少执行一次while先判断后执行可能一次都不执行。笔试里常见的变体是问你如果读入一个数直到遇到负数用哪个循环更合适答案是do-while因为至少要读一个数再根据读到的数决定是否继续。这个细节用错了代码逻辑就会有边界Bug。const这个关键字更是一堆考点const修饰变量、const修饰指针、const修饰成员函数。其中“const指针和指向const的指针”的区别是每次笔试几乎必出的题。用一句话概括const在左边说明指向的对象是const的const在右边说明指针本身是const的。具体来说const int* p; // 指向const int的指针指针可以变指向的对象不能通过p修改 int* const p; // const指针本身指针不能变指向的对象可以修改 const int* const p; // 两者都不可变static关键字就更多了静态局部变量只初始化一次、存储在静态区、生命周期到程序结束静态全局变量限制作用域在当前文件静态函数只在当前文件可见C中static类成员属于类所有不属于任何对象。笔试里会考“下面代码输出什么”#include stdio.h void counter() { static int count 0; count; printf(%d , count); } int main() { for (int i 0; i 3; i) { counter(); } return 0; }输出是1 2 3。因为count是静态局部变量初始化只发生一次函数调用结束后内存不释放继续累加。这个机制在需要每个函数记住状态时非常有用但很多人考试时会想成“每次调用都初始化为0”。再补充一个笔试容易混淆的点sizeof和strlen的区别。sizeof是运算符在编译期计算类型或变量占用的字节数strlen是函数在运行期计算字符串长度不包含\0。所以对char buf[] hello来说sizeof(buf)是6包括\0strlen(buf)是5。如果对指针用sizeof比如char* p buf; sizeof(p)在64位系统上是8和字符串长度没关系。这个考点几乎每次必考一定要牢记。3. 算法与数据结构从字符串逆序到图算法3.1 字符串逆序输出三种写法背后考察的功底回到“字符串逆序输出c”这个热搜词。这道题在校招笔试里非常经典看似基础但不同写法能看出不同的水平。第一种是刚才说的双指针原地逆序这个是最推荐的空间复杂度O(1)时间复杂度O(n)。第二种是递归写法虽然代码很短但空间复杂度O(n)而且如果字符串太长会导致栈溢出笔试中除非面试官明确要求否则不建议写#include stdio.h void reverse_recursive(char* str, int left, int right) { if (left right) return; char temp str[left]; str[left] str[right]; str[right] temp; reverse_recursive(str, left 1, right - 1); }第三种是借助额外数组从尾部向头部拷贝。这个写法空间复杂度O(n)在笔试中通常不做首选但在允许使用额外空间且要求保持原字符串不变时可以考虑。为什么同一道题我要强调三种写法因为笔试阅卷时好的解法会直接拉开和普通考生的差距。双指针不仅效率高而且没有依赖额外的C库函数展示了你对内存和指针的控制能力。这种“溢出屏幕的熟练度”在校招复盘中非常加分。另外字符串逆序还有一种考法是“逆序输出但单词顺序不变”比如输入“I am a student.”输出“student. a am I”。这道题很多人在笔试里倒下了。正确做法是分两步先对整个字符串逆序再对每个单词内部逆序。先逆序整个字符串得到“.tneduts a ma I”再把每个单词内部反转回去就得出了正确答案。这种“先整体后局部”的思路在算法题里经常出现比如数组旋转、区间反转都是同一个套路。3.2 冒泡排序为什么校招还爱考这种“简单”算法“冒泡排序c语言”能在热搜里出现一点都不奇怪。几乎所有C语言笔试都会涉及排序而冒泡排序出镜率最高的原因是它足够简单适合用来考察候选人的代码规范性你能不能写出没有Bug的版本能不能在版本里加上优化基础版冒泡排序代码不复杂#include stdio.h void bubble_sort(int arr[], int n) { for (int i 0; i n - 1; i) { for (int j 0; j n - 1 - i; j) { if (arr[j] arr[j 1]) { int temp arr[j]; arr[j] arr[j 1]; arr[j 1] temp; } } } }但校招笔试绝对不会到这里就完了它有很多变着花样的考法如何判断某轮没有交换就提前结束加一个flag标记即可这样在数组已经有序时时间复杂度降为O(n)。冒泡排序是稳定的吗是因为只有相邻元素且仅在大于时才交换相等的元素不会交换位置。冒泡排序的最好情况、最坏情况、平均时间复杂度分别是O(n)、O(n^2)、O(n^2)。如果数组里有两个元素相等排序后它们的相对顺序会变吗不会。在实际做这套练习题的时候我建议你除了冒泡排序也把插入排序、选择排序、快速排序、归并排序的习惯写法都过一遍。因为笔试中“请用你熟悉的排序算法对数组排序”这种题选哪个算法写、能不能解释清楚为什么选它都会影响阅卷者的判断。你要是选快速排序至少要说明平均时间复杂度和为什么最坏情况会退化到O(n^2)你要是选归并排序最好提一下需要O(n)的额外空间所以不适合内存受限场景。3.3 进阶考点迪杰斯特拉与相关变形题“迪杰斯特拉c”这个热搜词说明有人在刷图算法。迪杰斯特拉Dijkstra算法是单源最短路径的经典算法校招笔试里考它的频率不低但通常不会直接让你默写而是给你一个图让你手算最短路径或者给你代码填空考的是你是否理解它的贪心策略和更新过程。迪杰斯特拉算法的核心几点贪心地每次从未处理的节点中选一个距离起点最近的点然后松弛它的邻接边适用于所有边权非负的图用邻接矩阵实现时时间复杂度O(V^2)用优先队列优化后是O((VE)logV)。考试中最常被问的坑是如果图里有负权边怎么办答案是迪杰斯特拉算法不适用需要用Bellman-Ford或者SPFA。如果你只背代码不理解原理这种变体题基本答不出来。我在准备这类题目时的小建议是光看代码没用一定要自己在纸上跑几遍算法的过程。比如画一个5个节点的图从节点0出发手动列出距离数组的每一步更新过程直到所有节点都被访问。这个过程做完你对算法里visited数组、dist数组、优先级队列的用法会理解得非常透。除了迪杰斯特拉图相关的常考类型还有拓扑排序判断有向图是否有环、最小生成树Kruskal和Prim、并查集处理连通分量问题、深度优先/广度优先遍历。爱奇艺这类视频网站业务里最短路径算法在有向图调度、推荐链路分析中也有实际用武之地出题人会下意识往这个方向倾斜。4. 笔试环境准备VS Code配置C/C环境和现场调试4.1 一套顺手的环境配置能省下大量时间备考过程中“vscode配置c/c环境”这个热搜词真不是没道理。很多同学在本地写C/C代码时连编译运行都找不到路径等到笔试时手忙脚乱。校招笔试很多是在线OJ但碰上有本地IDE的笔试或者你自己在本地练习时一个能直接跑起来的环境非常关键。我推荐的方案是用VS Code搭配MinGW-w64或者MSVC。具体步骤如下安装VS Code安装C/C扩展由Microsoft官方提供。安装编译器。Windows下推荐MinGW-w64Linux下直接用gcc/g。配置tasks.json定义编译任务。一个典型配置{ version: 2.0.0, tasks: [ { label: C/C: gcc build active file, type: cppbuild, command: /usr/bin/gcc, args: [ -fdiagnostics-coloralways, -g, ${file}, -o, ${fileDirname}/${fileBasenameNoExtension} ], options: { cwd: ${fileDirname} }, problemMatcher: [ $gcc ], group: { kind: build, isDefault: true } } ] }配置launch.json让F5能直接调试{ version: 0.2.0, configurations: [ { name: C/C: gcc build and debug active file, type: cppdbg, request: launch, program: ${fileDirname}/${fileBasenameNoExtension}, args: [], stopAtEntry: false, cwd: ${fileDirname}, environment: [], externalConsole: false, MIMode: gdb, setupCommands: [ { description: Enable pretty-printing for gdb, text: -enable-pretty-printing, ignoreFailures: true } ], preLaunchTask: C/C: gcc build active file, miDebuggerPath: /usr/bin/gdb } ] }Linux下路径是/usr/bin/gcc和/usr/bin/gdbWindows下要换成你MinGW的安装路径比如C:\MinGW\bin\gcc.exe。这些配置文件准备好之后在VS Code里打开一个.c文件按F5直接编译、运行、调试跟在线OJ的体验就差不多了。调试器在笔试中也能救命。在线OJ没法用调试器但如果你写的代码在自己的环境里跑一遍没问题再提交到OJ通过率会高很多。我自己的习惯是每道编程题先在本地写好、跑样例、再换几组边界测试数据空数组、单元素、大数值没有问题了再粘贴到OJ里。4.2 编译报错与边界条件笔试现场最致命的细节很多人在笔试里代码逻辑是对的但编译不过原因是少了头文件或者命名冲突。比如用了strlen但没写#include string.h用malloc但没写#include stdlib.h用bool但没写#include stdbool.hC语言里。这些细节准备充分了一次就能记住。还有一种容易踩的坑是“返回局部变量地址”的问题以及“数组越界写入”的问题。特别是有时候你开了一个长度为n的数组却想给下标n赋值这在本地运行时编译器往往不会报错但OJ会判“Runtime Error”。平时写代码的时候我建议始终用循环边界小于数组长度这个习惯来约束自己。输入输出也是笔试里很重要的一环。很多C语言题目的输入格式是“第一行一个整数n第二行n个整数”你不能提前知道n的上限。常见的处理方式是#include stdio.h #include stdlib.h int main() { int n; while (scanf(%d, n) ! EOF) { int* arr (int*)malloc(n * sizeof(int)); if (arr NULL) return 1; for (int i 0; i n; i) { scanf(%d, arr[i]); } // 处理逻辑 free(arr); } return 0; }特别注意while (scanf(%d, n) ! EOF)这个写法。有些题目要求一直处理到文件结束如果不写这个循环只处理一组数据那通过率绝不会超过一半。还有如果输入结尾有换行符scanf会吃掉它不用额外处理但如果用gets或者fgets读取带空格的字符串就得注意换行符还留在缓冲区的问题。边界条件这块我列了一个自测清单笔试提交前挨个过一遍数组为空n0数组只有一个元素n1输入数据已经是正序或逆序字符串为空字符串涉及整数运算时会不会溢出int最大值2147483647涉及到除法时除数是否可能为0循环结束后是否有可能漏处理最后一个元素习惯这样自检之后代码一次通过率会明显提升这在分秒必争的在线笔试中是实打实的差距。5. 高频陷阱与排查实录帮你们提前踩完这些坑5.1 数组越界、空指针和内存泄漏我整理了一下C/C笔试做错题最集中的原因有三个数组越界、空指针、内存泄漏。数组越界的典型场景是定义int a[n]然后循环里写了i n最后把a[n]写了数据。这个错误在本地跑可能完全没有症状因为越界写的是紧挨着数组的内存不会马上崩掉但到了OJ里就可能因为踩到关键数据导致“Segmentation fault”。另外动态申请的数组越界在释放时最容易暴露——你越界写破坏的是malloc的内存管理信息free的时候直接崩溃。空指针的典型场景是malloc后没有检查返回值就使用。如果内存申请失败malloc返回NULL继续对NULL解引用就会崩溃。笔试里很多“以下代码哪里有问题”的题目就是专门为这种场景设计的。防御性编程的习惯要养成所有指针在用之前先判断是否为空所有申请的资源用完要释放。内存泄漏在笔试中通常不会直接导致编译错误或运行崩溃但会有“这段代码有什么问题”的问答题。比如函数内malloc了一块内存返回时没有释放也没有把指针暴露给外部去释放。一个负责任的说法是内存泄漏会导致长期运行的程序内存占用持续增长最终OOMOut Of Memory甚至被系统杀掉。这三类问题虽然常被单独列出来考但在实际项目中往往是叠加出现的。我建议大家准备一套固定的排查思路先看所有数组下标是否在合理范围内再检查指针是否可能为空或悬空最后确认任何申请的内存都有对应的释放且释放只执行一次。5.2 编译报错和逻辑Bug的快速定位思路笔试中最浪费时间的不是写代码而是排查问题。我在准备过程中总结了一套快速定位套路第一步编译报错先看黄色波浪线。VS Code里C/C扩展会直接标出常见语法问题比如少分号、少括号、变量未声明根据提示改即可。如果是类型不匹配先看是不是少写头文件比如用了size_t但没有#include stddef.h。第二步逻辑Bug用printf大法。在关键节点输出中间变量的值比如循环里i、j的值、数组交换前后的值、字符串处理的每个阶段结果。这道题的核心变量是什么先打印它。很多问题肉眼一看打印结果就明白了。第三步边界情况单独测试。如果代码逻辑看起来都对但OJ就是判错那大概率是边界没覆盖到。把上面提到的自测清单过一遍尤其注意数组长度为1和空字符串的情况这两个可以救回不少分。还有一类隐藏很深的问题是整数溢出。比如计算数组累加和int类型的上限是21亿多如果数组中有10万个100万的数总和会高达1000亿超出int范围要么用long long要么对结果取模时随时取模。这种问题不会在你手动测试时暴露但OJ的大数据测试用例一上去就挂了。5.3 备考的综合建议做题只是最后一步写到这里我想多说一点。很多人备考校招笔试题习惯性地以“刷题数量”为目标但实际上更有效的方式是“以知识点为单位”做整理。每做一套题把错题对应的知识点记下来比如const指针又弄混了、strcpy和strncpy的边界又忘了、冒泡排序的flag优化没写出来然后集中刷这一类题型直到彻底掌握为止。另外C/C语言基础比较薄弱的同学不建议一上来就刷真题。先把翁恺老师的C语言练习题过一遍或者把《C语言程序设计现代方法》的指针、字符串、内存这几章啃完再去做校招真题才会顺畅。否则就会出现“看答案都懂自己做却不知道怎么下手”的挫败感。实操建议的话每周固定两三次的OJ训练是很有必要的。像LeetCode上的数组、字符串、链表、二叉树题目都可以选C语言做。输入输出格式和在线OJ的环境提前熟悉了真正笔试时就不会因为“读取多组数据”这种问题卡住。最后说个小技巧笔试前一天的晚上不要做新题把之前的错题和常用代码模板重新看一遍。模板包括快排、冒泡排序、字符串逆序、单链表反转、二分查找、二叉树前中后序遍历这些代码要能默写出来。我自己就是靠这个习惯在好几场校招笔试里都稳定发挥最后顺利拿到了几个满意的Offer。希望这篇文章能帮你在备考路上少走一些弯路。