ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

京东C/C++校招笔试复盘:指针内存与字符串算法全解析

京东C/C++校招笔试复盘:指针内存与字符串算法全解析 2019年春招季已经过去很久了但直到现在还有不少准备校招的同学翻出这份京东C/C开发类试卷来复盘。我当年也做过这套题印象最深的是它不玩虚的选择题里全是那种“你觉得自己会、一选就错”的经典陷阱编程题又不给任何花哨背景纯拼基本功。虽然这份卷子已经过去几年但C语言笔试的考点其实高度稳定指针、内存、字符串、排序算法这些核心内容年年都考变化的只是出题外衣。所以如果你现在正在准备C/C方向的校招这份试卷依然有非常高的参考价值。很多人拿到这份卷子第一反应是“范围太杂”其实拆开看考点非常集中。我把它复盘一遍结合我自己做题时的思路和踩过的坑把每一类题型背后的考点逻辑、易错点、以及考场上实际应该怎么应对都整理出来。无论你是刚开始刷题还是已经进入冲刺阶段这份复盘应该都能给你一些实在的参考。1. 试卷定位与考点分布复盘先给没做过这套题的同学说下整体观感。试卷整体分四块单选题、多选题、简答题和编程题时间大概90到120分钟。单选题大概10到15道多选5道左右简答2到3道编程题2道。题量不算大但每道题都扎在C语言最容易出问题的地方没有一道是白给的。单选题范围覆盖了变量作用域、指针运算、运算符优先级、数组与指针的关系、结构体对齐、宏定义、位运算。多选题则集中在const、static、volatile这些关键字的组合效果以及内存分配的生命周期问题上。简答题一般会让你手动模拟一段程序的输出结果或者解释某个代码片段的问题所在。编程题通常是字符串处理和链表操作难度接近LeetCode中等偏下但手写代码的环境下很多人会卡在细节上。2019年那个时间点校招C/C岗位的笔试已经比较成熟了考察重点和现在基本一致底层内存模型、指针使用、标准库函数的内部机制、常见算法的手写能力。京东这套题没有标新立异但它把C语言笔试的典型考点都覆盖到了所以复盘价值很高。考点热词和实际题目的对应关系如下“字符串逆序输出”和“字符串函数”对应编程题和简答题“指针”和“C语言内存管理”对应选择和简答中的重头戏“冒泡排序”和“字符串逆序”这类基础算法对应编程题低难度题“while和do-while区别”对应选择中的语法陷阱题“C语言文件读写”偶尔出现在简答题里考察fopen/fread/fwrite的用法细节所以别看热词杂核心就是“指针 内存 字符串 基础算法”这四板斧。1.1 四种题型的定位逻辑出题人为什么这样安排我个人的理解是单选题负责“快速筛选”用一些看似基础但容易混淆的知识点过滤掉基础不牢的候选人。多选题负责“深度考察”一道题同时牵扯多个知识点比如同时考查const char *p和char *const p的区别顺带问你哪种写法能修改指向的内容。简答题负责“验证思维过程”看你能不能把代码执行过程一步步推导出来。编程题则负责“测试工程底线”毕竟招的是开发岗代码能不能跑起来、边界条件能不能考虑到这些是硬指标。这套题还有一个特点就是比较重视“手写能力”。现在的笔试很多都是OJ形式编译不通过就零分但那时候京东的卷子还保留了一部分手写题。手写代码和敲代码是两种状态没有IDE的自动补全和编译提示很多平时写得出来的代码在纸上就容易翻车。这一点在后面的编程题部分我会详细展开。1.2 从考点分布看C开发岗的能力要求做完这份卷子再回头去看它的考点分布基本能勾勒出京东C开发岗对校招生的能力预期。一个是“手册级掌握”标准库常用函数的用法、边界条件、返回值不能只停留在“用过”的层面。另一个是“微观理解”数组名和指针的区别、栈和堆的区别、内存碎片怎么产生这些底层细节要能讲清楚。还有一个是“代码敏感度”一段代码摆在面前能不能快速看出潜在问题——悬空指针、缓冲区溢出、资源泄漏。这套能力模型放到现在也不过时甚至更重要了。所以把这份卷子吃透不只是在准备一场考试而是在补C语言开发的基本功。2. 基础语法考点看着都会一选就错的经典陷阱这套卷子的单选和多选很多错误选项都是照着高频混淆点设计的。我把自己复盘时整理的几个高频陷阱列出来这些不只是为了应付这场笔试后续面试被问到“C语言有哪些容易出错的点”时也能直接用上。2.1 指针声明与const修饰符的组合含义const和指针组合在一起时是笔试最爱的出题点也是日常开发中很容易绕晕的地方。const int *p、int const *p、int *const p这三个声明完全是不同含义。const int *p表示p指向一个const int你不能通过p去修改它指向的值但p本身可以指向别处。int *const p表示p本身是const的一旦初始化就不能再指向别的地址但你可以通过p修改它指向的内容。int const *p和const int *p是等价的const修饰的是int。这道题的考点在面试里经常升级为“const char *p和char *const p有什么区别”。口头回答很多人能说对但落到笔头选项里就容易看反。我当时的判断方法是先找到const修饰的是谁const在*左边修饰的是指针指向的类型const在*右边修饰的是指针本身。这个规则简单做题最快。2.2 数组名与指针的微妙关系选择题里经常出现类似这样的代码int arr[5] {1, 2, 3, 4, 5}; int *p arr; printf(%d\n, *(p 3));答案显然是4但如果你把p之后再算一次很多人就乱了。数组名在绝大多数表达式里会退化为指向首元素的指针但在sizeof和运算符里不会。所以sizeof(arr)是5*420而sizeof(p)是8或者4取决于平台。arr的类型是int(*)[5]也就是指向整个数组的指针对它1会跳过整个数组。这道题背后是“数组名和指针到底是不是一回事”的本质问题。答案很明确不是。数组名是一个标识符它在表达式中会退化但它本身有类型和大小信息。这个区别在笔试里经常通过sizeof和来出题。2.3 while和do-while的循环时机差异热词里有“c语言while和do-while区别”这确实是基础题里的常客。两者最核心的区别是执行时机while先判断条件条件为假一次都不执行do-while先执行一次循环体再判断条件所以至少执行一次。考场上经常用一段代码来混淆int i 0; while (i 0) { i; } printf(%d\n, i); // 输出0 int j 0; do { j; } while (j 0); printf(%d\n, j); // 输出1这个区别本身不难但很多人做题时会把do-while的语法结构看错或者忘了它最后有个分号。在简答题中如果让你分析某个循环执行了几次务必要先确认使用哪种循环结构。2.4 结构体内存对齐问题结构体对齐是笔试里很经典的一道题京东的选择题里出现过类似结构。题目一般长这样struct Test { char a; int b; char c; };问sizeof(struct Test)是多少。直观想法是1416但答案是12。原因在于内存对齐规则结构体成员的偏移量必须是成员自身对齐数的整数倍结构体的总大小必须是最大对齐数的整数倍。char对齐数是1int对齐数是4编译器会在a后面填充3个字节让b从偏移4开始c占1字节后结构体总大小要从9补齐到最大对齐数4的倍数也就是12。如果调整成:struct Test2 { char a; char c; int b; };结果是8a占偏移0c占偏移1b从偏移4开始总大小8。同样的成员不同的排列顺序节省了4字节。这个细节在笔试里常被用来考“内存布局理解”在嵌入式开发中更是直接关系到内存占用和通信协议解析。做题窍门就是记住两句话成员偏移要对齐结构体总大小要补到最大对齐数的倍数。2.5 运算符优先级和宏定义的坑选择题里还有一类必考题表达式求值和宏替换。比如#define SQUARE(x) x * x int a SQUARE(2 3);结果是11而不是25因为展开后是23*23。正确的宏应该写成#define SQUARE(x) ((x) * (x))。这种题属于“知道就秒选不知道就想不通”的类型所以凡是遇到宏定义先在心里做一次纯文本替换不要急着算结果。运算符优先级则是另一类送命题。比如*p到底是(*p)还是*(p)答案是后者因为后置的优先级高于解引用*。这类题我一直建议在考前集中过一遍优先级表不用背全但把“括号、后置/--、单目运算符、乘除、加减、移位、关系、相等、位运算、逻辑、三目、赋值、逗号”这个顺序记住基本就够用了。3. 字符串与内存管理考点纸上代码最容易翻车的地方字符串是整套试卷里分量最重的部分之一原因很简单C语言没有原生的字符串类型所有字符串操作都依赖字符数组和指针任何一个细节没处理好轻则结果不对重则程序崩溃。而内存管理则是C开发岗区别于其他语言开发岗的核心考察点。3.1 常用字符串函数的边界细节热词里有“c语言字符串函数”笔试中经常直接考strcpy、strcat、strcmp、strlen这几个函数的内部实现特点和边界行为。strlen计算字符串长度时不包括结尾的\0。这个很多人知道但遇到char str[100] {hello}时strlen结果是5sizeof(str)是100这两者一旦混淆后面所有代码分析都会出错。strcpy拷贝时会把源字符串的\0一起拷过去所以目标缓冲区必须足够大否则就会缓冲区溢出。strcat是在目标字符串末尾追加它会先从目标字符串找到\0然后从那个位置开始拷贝同样有缓冲区溢出的风险。strcmp按字典序比较返回值是负数、0、正数分别表示小于、等于、大于。笔试简答题里有一种典型问法给出如下代码指出问题并改正char *p hello; strcpy(p, world);问题是p指向字符串字面量它存储在只读数据段试图修改它会导致未定义行为通常是段错误。正确做法是char p[6]或char p[] hello。这种题就是考你有没有“字符串字面量不可修改”的意识。3.2 字符串逆序输出的多种写法热词里有“字符串逆序输出c”这个题目出现了很多次而且它不只是一道入门题在笔试中它会变着花样考。最基本的版本是给定字符串hello输出olleh。要求不使用额外的数组原地逆序。这里有一版可复现的写法#include stdio.h #include string.h void reverse_inplace(char *s) { int left 0; int right strlen(s) - 1; while (left right) { char tmp s[left]; s[left] s[right]; s[right] tmp; left; right--; } } int main() { char s[] hello; reverse_inplace(s); printf(%s\n, s); return 0; }这里有几个细节值得展开。第一s必须是可修改的字符数组不能是char *s hello否则运行时直接崩溃。第二right的初始值是strlen(s) - 1很多人写成strlen(s)导致最后把\0换到前面来输出就直接断掉了。第三交换用的是临时变量法也可以写成异或交换但笔试里写临时变量最稳妥异或交换容易在边界情况出错且代码可读性差。考场上这个题还有升级版要求逆序输出但不改变原字符串只打印结果。这时可以用指针从末尾向前遍历。再升级一版是逆序整个字符串的同时保持单词内部顺序不变比如hello world变成world hello。做法是先整体逆序再逐个单词逆序这个思路在面试题中也很常见。3.3 堆内存管理的核心要点热词里有“c语言内存管理”和“c语言内存管理”说明这是共识性重点。选择题中经常出现这样一段代码char *get_memory() { char p[] hello; return p; }这个函数返回了局部数组的地址但数组在函数返回后就失效了指针变成悬空指针。调用方如果使用这个指针结果是未定义的。正确的做法是返回指向堆内存的指针或者在调用方传入缓冲区。堆内存的使用又有自己的坑。malloc申请后要检查返回值不能用完不freefree之后要把指针置为NULL避免重复释放这些都是老生常谈但笔试里它会把场景包装得很隐蔽。比如void func() { int *p malloc(sizeof(int) * 100); // 中间有 return free(p); }如果函数在free之前有多个return出口就会出现内存泄漏。正确做法是统一在函数出口释放或者用goto清理代码段。这个考点在面试中会变成“如何设计一个避免内存泄漏的函数”我一般建议统一入口统一出口的编码风格。还有一个常被忽视的点malloc申请的是未初始化的内存里面是随机值。如果直接使用可能得到垃圾数据。calloc会初始化为0realloc则会移动或原地扩展内存。这些函数的复杂度不算高但要能说清楚各自的适用场景。3.4 栈与堆的区别和生命周期简答题中有一道经典题简述堆和栈的区别。这道题几乎每次笔试都会出现答得好不好直接影响面试官对你基本功的判断。大致答题要点可以这样组织分配方式上栈由编译器自动分配和释放堆由程序员手动申请和释放。分配效率上栈的分配只是移动栈指针速度很快堆的分配需要查找空闲内存块可能触发系统调用速度较慢。空间大小上栈空间一般只有几MB堆空间可以大到几个GB。生命周期上栈变量在函数返回时自动销毁堆变量从malloc到free期间一直存在。数据结构上栈是后进先出堆在分配上更像一个自由存储区没有固定顺序。答题时最好结合例子说明比如递归函数中局部变量在栈上不断压栈递归过深会导致栈溢出而用malloc申请大块内存可以放在堆上。在项目开发中大数组、动态链表、大对象的存储都应该优先考虑堆。4. 算法题复盘笔试里的送分题与陷阱题算法部分是整套试卷的压轴块京东这套题的编程题一般不会出太偏的算法基本围绕字符串和链表展开。冒泡排序、字符串逆序、链表反转、查找数组中重复元素这些题目本身不难但考场上状态不一样容易写出一堆低级错误。我把当时做题的思路和踩坑点整理成可直接复用的解题模板。4.1 手写冒泡排序的边界控制热词里有“冒泡排序c语言”这基本是校招笔试的看家题。别觉得它简单手写时能一次写对的人比例并不高。先给一版模板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 tmp arr[j]; arr[j] arr[j 1]; arr[j 1] tmp; } } } }写错最多的地方是内层循环的边界。如果你写成j n - 1排序结果大概率也对但会多做很多次无意义的比较。真正的问题是很多人会写成j n - i导致数组越界因为arr[j 1]最大下标变成n - i当i0时是n越界了。所以内层循环边界务必写成j n - 1 - i。如果题目额外要求“优化冒泡排序”可以在每一轮设置一个flag如果这一轮没有发生任何交换说明已经有序直接跳出。这个优化在笔试中属于加分项可以顺手带上。冒泡排序的时间复杂度是O(n^2)空间复杂度O(1)是稳定排序这些信息在简答题里常见最好背熟。4.2 链表反转的迭代实现链表操作几乎是C开发岗必考京东这道编程题出现过链表反转。考场上最稳妥的是迭代实现思路非常直接用三个指针prev、current、next按顺序遍历链表逐个翻转节点的next指向。struct ListNode { int val; struct ListNode *next; }; struct ListNode* reverse_list(struct ListNode *head) { struct ListNode *prev NULL; struct ListNode *curr head; while (curr ! NULL) { struct ListNode *next curr-next; curr-next prev; prev curr; curr next; } return prev; }关键点在于必须先保存curr-next再修改curr-next否则修改之后原链表就断了你找不到下一个节点了。这个坑我在纸上写过很多次每次都是栽在没有保存next上。如果题目要求递归实现也可以写但迭代版本更稳不容易爆栈。链表类题目的边界条件也要检查空链表、只有一个节点、链表长度为2这三种情况最好都自己跑一遍。迭代写法天然能处理空链表和单节点的情况而递归写法在链表很长时会有栈溢出风险笔试中不推荐优先考虑。4.3 查找数组中的重复元素京东笔试编程题还出现过一个场景比较明确的题给定一个长度为n的数组里面数字范围是0到n-1判断是否存在重复数字。这类题有个非常经典的解法因为数字范围受限我们可以把每个数字放到它对应的下标位置上去如果发现目标位置已经被相同的数字占住就找到了重复。int find_duplicate(int *nums, int n) { for (int i 0; i n; i) { while (nums[i] ! i) { if (nums[nums[i]] nums[i]) { return nums[i]; } int tmp nums[i]; nums[i] nums[tmp]; nums[tmp] tmp; } } return -1; }这个解法的时间复杂度是O(n)空间复杂度是O(1)比排序后遍历更优。考场上要是能写出这个方案是明显加分项。如果题目不满足“数字范围受限”的条件也可以用排序或哈希但判卷时看重的往往是原地算法的技巧性。4.4 程序模拟题的正确答题姿势简答题里有一类“写出输出结果/指出错误”的模拟题这是整份卷子里最容易失分的地方。我建议按固定流程来做先标注变量类型再画出内存示意然后按行模拟最后检查边界。举个例子题目给一段这样的代码#include stdio.h int main() { int a 10; int *p a; *p 20; printf(%d\n, a); return 0; }模拟流程应该是a是int初始值为10p是指向int的指针保存a的地址*p20表示通过指针修改a所在内存的内容所以a变成20最后输出20。这类题难一点会把指针的指针、数组和结构体加进去但答题流程是不变的。还有一个答题技巧碰到地址输出比如printf(%p, p)不要试图换算地址值直接按“输出的是十六进制地址”来说明即可。判卷老师更关注你是否理解p和a的关系而不是具体数值。5. 编程题实操从读题到AC的完整流程写编程题是整套试卷中最考验综合能力的环节。很多人题目会做但考试时间不够或者代码写出来了但细节错漏百出。我复盘自己在这套卷子上踩过的坑整理了一套稳定的做题流程。5.1 读题阶段的三个关键动作第一个动作圈出数据范围。比如题目说n ≤ 1000那O(n^2)的算法可以直接写如果n ≤ 10^5O(n^2)就危险了得想优化方案。第二个动作确认输入输出格式。这个决定了你的代码框架怎么搭比如多组输入还是单组输入输出有没有空行要求。第三个动作思考边界条件。空输入、只有一个元素、所有元素都相同、元素已经有序这些情况算法是否正确。京东这套卷子的编程题都是经典题数据范围不会太极端但读题时养成这个习惯能避免很多低级失误。如果你在平时OJ刷题时发现同样的思路别人AC你WA基本就是边界条件没想全。5.2 代码实现阶段的注意事项手写代码阶段有几个注意事项值得强调。先确认你要修改的字符串是否可写。如果你写了char *s hello然后又尝试修改s[0]在部分编译器上会直接崩溃在另一些编译器上可能正常运行但这都是未定义行为。笔试改为数组更安全除非题目明确要求指针。再确认循环退出条件。很多人在写while循环时退出条件写反或者漏了等号。比如查找链表中某个节点条件写成while (p-val ! target)当p变成NULL时再访问p-val就崩了。正确写法是while (p p-val ! target)。还有free指针之后要马上置NULL。这虽然是好习惯但在笔试中更重要是体现你意识到这个问题。一道题写完主动检查malloc和free是否配对比等程序运行出错再排查要高效得多。5.3 手写代码如何避免低级错误手写代码时没有IDE没有任何提示很容易出现拼写错误或括号不匹配。我的建议是写代码时保持变量名简短且统一比如循环变量用i、j、k指针用p、q临时变量用tmp这样写起来快也不容易混。另一个是每写完一个函数用几组简单用例在心里跑一遍。比如排序算法用3, 1, 2跑一次链表反转用1 - 2 - 3跑一次。不需要每一步都写出来但心里要有数。还有一个独门技巧代码写得慢不要紧先把整体骨架搭出来再补细节。比如先写main函数、定义变量、调用函数最后再补函数体的实现。这样即使时间不够判卷老师也能从代码框架上看出你的思路是对的能拿到部分分数。5.4 应对不熟悉的环境和编译报错笔试环境一般分两种在线OJ和本地IDE加在线提交。在线OJ的编译器可能跟本地的版本不一样C语言标准也不同。如果题目要求支持C11而环境默认是C99写代码时就要避免使用C11才有的特性。另一个常见问题是本地跑得好好的代码提交上去却编译不过多是因为没有引入需要的头文件比如用了malloc但没写#include stdlib.h。这种细节虽然看不出难度但很影响实战成绩。从卷面的角度讲卷子上有一两道编程题写不出来不代表整份卷就完了。把能拿的分拿稳把思路写在代码注释里有时候也能捡到一些分数。毕竟笔试只是初筛如果后面面试表现好笔试稍有短板也有机会翻盘。6. 备战校招C开发岗的另外几点建议把这份试卷复盘完我想说点更宏观的东西。C语言笔试不仅仅是刷题它是在查你大学几年到底有没有真正理解这门语言。很多人上课时用的是C语言做项目时用的是Java或Python到了校招想转回C开发岗结果发现自己对C语言的记忆只停留在“能看懂”的层面一到手写就露馅。这是最常见的备战误区。6.1 刷题的正确打开方式刷题不要贪多要按“专题”推进。比如花两周集中刷字符串相关题逆序、反转、子串查找、分词、字符串转整数。每道题做完不要急着下一题先把这道题涉及的知识点列出来比如指针修改、缓冲区边界、动态分配然后针对这些薄弱点查漏补缺。这样一轮下来比盲目刷两百道题效果要好。我在准备春招时就是这么过的每天只刷两道大题但每道题都用书面形式写好再对照标准答案找差别。重点不是“做对”而是“做出来并且知道为什么这么做”。C语言的知识点环环相扣指针理解不深字符串和内存管理必然出错内存管理不牢链表和树的题目就会各种泄漏和崩溃。所以一旦发现自己某个专题总是出错就要回到最基础的概念重新读一遍。6.2 从笔试到面试的知识迁移笔试往往只是第一步京东这类公司笔试通过后还有技术面试。我在面试时就被问过“你笔试里用了这个解法还有没有更优的解法”。这时候如果你只是背了标准答案就很难应对。所以备考时就要养成追问自己的习惯这道题为什么用两个指针还有没有空间O(1)的解法如果数据量扩大十倍怎么改这些追问的价值不只是面试它也是真正理解算法的过程。比如上面说的链表反转会写迭代版还不够要能说清楚为什么prev要从NULL开始为什么先保存next以及递归版和迭代版的区别。把这些想明白了笔试里的简答题和面试里的追问都能应对。6.3 用项目经验佐证语言功底如果你有实际项目经验笔试后可以主动把项目里的C语言细节梳理一遍你在项目里是怎么管理内存的有没有遇到内存泄漏或者段错误怎么定位和解决的用了哪些调试工具这些具体问题的答案远比“我熟练掌握C语言”有说服力。校招笔试只是起点真正的开发能力还是在写代码和查问题的实战中积累起来的。我和一些后来入职京东的同学聊过他们反馈实际工作里最常用的恰恰是笔试里的那些基本功结构体内存布局、指针使用、字符串处理、多线程之间的数据同步。笔试考这些不是因为出题人保守而是因为工程里真的每天都要用到。6.4 哪些方向可以继续深入C开发岗涉及的方向很广嵌入式、网络、数据库内核、游戏引擎、音视频、中间件底层每个方向对C语言的要求略有不同。如果你通过笔试进入面试建议根据岗位方向准备对应领域的知识。嵌入式方向常考寄存器操作、位域、volatile的作用网络方向常考socket编程、数据包解析、字节序转换音视频方向则更关注数据缓冲、内存拷贝优化、并发同步。并不是说所有知识点都要在笔试前学完但如果你能结合试卷暴露出的薄弱点再往自己目标方向深挖一层校招的通过率会明显提升。我自己经历过校招也做了几年的开发回头看这套2019年的京东C/C开发类试卷最大的感触是它没有一道超纲题但想拿高分并不容易。原因在于它考的不是“会不会”而是“熟不熟”。一个知识点你只是听过、看过和你在限定时间内能准确写出来是两种完全不同的状态。所以备考的关键不是收集多少份试卷而是把每一道题背后的知识点吃透直到变成条件反射。最后分享一个我自己的备考小习惯每次做完一份试卷不要急着对答案先把每一道题的知识点列一张表标注出“掌握”“模糊”“完全不会”三个等级。隔一天再回来看“模糊”和“完全不会”的部分重新做一遍。这个过程不用重复太多轮三轮下来你的状态基本就能达到上考场的水平了。祝正在备战校招的你也能拿到心仪的offer。
返回列表