拓冰建站拓冰建站
首页 / 资讯中心 / 正文

360内推C语言笔试备考:指针、内存与手写代码实战解析

1. 拿到内推资格之后我是怎么准备这场笔试的1.1 内推笔试和统招笔试不是一回事2016年那会儿校招的主流玩法还是网申统一笔试面试内推渠道虽然存在但远没有现在这么普及。当时360的内推一般是找师兄师姐或者论坛上认识的前辈要一个内推码然后去官网投递简历会走独立通道笔试安排也会比统招早几天。很多同学以为内推笔试会简单一些这是个挺常见的误解。内推只是让你的简历被看到的时间提前筛简历的环节可能宽松一点但笔试的题量和难度基本不会打折甚至因为内推批次较早、岗位名额尚不确定题目覆盖面会更宽。我当年就吃过这个亏以为内推是优先进面试结果笔试照样刷人而且刷得毫不留情。所以第一个要纠正的心态是内推不等于免笔试更不等于降难度。准备内推笔试要用和统招笔试一模一样的认真程度去对待唯一的优势是你的试错成本更高、还有机会在正式批次前通过面试积累经验而已。1.2 我锁定的三个复习方向确定要参加360的C研发工程师内推笔试后我给自己做了个简单的复习规划。当时距离笔试大概只有两周时间不算充裕所以我没打算面面俱到而是按照笔试的常规出题规律把精力压在了三个方向上。第一个方向是C语言本身的扎实度包括指针、数组、字符串、结构体、内存管理、预处理器、类型转换、作用域和存储类别。这些是C笔试题永远绕不开的核心我给自己定的标准是凡是《C程序设计》和《C和指针》里重点讲过的概念都能不看笔记讲清楚并且能写出对应的代码片段。第二个方向是经典算法和数据结构的手写实现。笔试编程题不会考偏题怪题链表反转、字符串逆序、排序、二分查找这类最常出现。我要的不是看过会做而是拿到题就能在规定时间内写出完整可运行的代码。这个能力的差距只有在限时手写时才能感受到。第三个方向是编译和运行的周边知识。比如sizeof和strlen的区别、宏定义展开带来的副作用、函数返回局部变量指针的陷阱、栈和堆的区别、内存对齐。这些知识在平时写业务代码时不一定天天用得上但笔试特别喜欢拿它们出选择题和改错题属于典型的你不踩坑就不知道坑有多深的类型。现在回头看这三个方向压得还是比较准的。后来的笔试题目绝大部分都落在这三个圈子里个别题目虽然形式新颖但底层考核的能力点没有变。2. 从试卷结构看C语言笔试的考察逻辑2.1 选择题概念细节决定基础分360的C研发工程师笔试试卷开头部分照例是选择题数量大概有二十来道覆盖面很广。这类选择题最典型的特征就是考细节你以为自己会细看每一道都藏着陷阱。我记得有一类题反复出现就是运算符优先级和结合性的组合判断。比如那种带有多个指针和自增运算的表达式*p怎么算*p和*p分别代表什么这类题如果不把优先级和结合性的规则背得滚瓜烂熟临场推导特别容易出错。说实话平时写代码时我很少会写出这么绕的表达式但笔试就是要考察你是否真正理解C语言的求值规则而不是只会照着书上的简单例子写。还有一类常考的细节是类型转换和整型提升。比如char和int做运算时的隐式转换unsigned和signed混用时比较的陷阱以及不同平台上数据类型占用的字节数。这些题不算难但必须建立在真正理解C语言标准的基础上靠死记硬背很容易被替换条件的方式蒙混过关。另外存储类别和作用域也是选择题的常客。static修饰局部变量和全局变量的区别extern的声明与定义的区别const修饰指针时修饰的是指针本身还是指向的内容这都属于C语言中看似简单实际容易混淆的知识点。复习的时候我专门把这些内容整理成了一页纸的对比表格考前反复看效果很直接。2.2 编程题手写代码的真正要求选择题之后是填空题和改错题再往后就是编程题。编程题的数量一般不会太多我记得大概是两到三题但每题的分值占比很大往往是决定是否进入面试的关键。编程题的考察重点往往是字符串处理、链表操作以及综合性的算法实现。字符串逆序是典型的入门题但笔试里的字符串题通常不会只要求输出逆序结果而是会加上各种限制条件比如要求原地逆序、不允许分配额外空间、要求处理空字符串和单字符字符串等边界情况。这种限制条件下的实现比单纯写一个倒序输出要难得多也更能看出考生的代码功底。链表相关的题目也很有代表性最常见的是反转链表有的题目会要求以递归方式实现有的要求迭代方式有的还要处理环形链表或者带随机指针的复杂链表。这类题的难点不在于算法思想而在于手写时对指针操作的清晰度尤其是链表反转中要注意保存下一个节点的地址否则指针一旦断开后面的节点就全部丢失了。除了这两类编程题还偶尔会涉及文件读写操作。C语言的文件操作不算难但手写时容易在细节上翻车比如忘记fclose导致文件句柄泄漏或者没有判断fopen的返回值就进行读写。笔试考察的往往不是你能不能打开文件而是你写出来的文件操作代码是否严谨、是否考虑到了错误处理。3. 几道印象深刻的题与完整解构3.1 字符串逆序一道题能看出三层功力字符串逆序这道题我在那次笔试里遇到了而且是妥妥的编程题。当时题目的要求大致是实现一个函数将输入的字符串在原地逆序不允许使用额外的数组。这个题表面上很简单很多人第一反应是遍历字符串、从尾部开始输出或者把字符串拷贝到另一个数组再倒序拷贝回来。但原地逆序这个限制条件直接把这两条路都堵死了考察的是双指针交换的写法。思路其实很朴素一个指针指向字符串开头一个指针指向字符串结尾交换两个位置的字符然后两个指针向中间移动直到相遇。写代码时有个细节很关键就是字符串结尾要怎么确定。如果用strlen求出长度再取最后一个字符的下标要记得最后一个字符是长度减一而不是长度本身。我当时还特意多写了判断如果字符串为空或者长度为1直接返回避免无意义的交换操作。这种边界判断虽然不是算法核心但在实际生产中非常重要笔试阅卷时也会注意你有没有这个意识。这道题还能延伸出更难一层——用递归实现字符串逆序。递归的思路是每次递归处理子串逐层交换首尾字符。这个版本的时间复杂度仍然是O(n)但空间复杂度变成了O(n)因为递归调用栈要占用空间。笔试时如果题目没有明确要求一般建议用迭代版本因为递归虽然看起来更高级但实际上在内存受限的嵌入式场景里并不划算。写完之后我还顺手检查了函数签名。如果题目要求void reverse(char *str)那函数就不能有返回值如果要求char *reverse(char *str)那就要注意返回指针的有效性。这类细节看似不起眼但在笔试阅卷时阅卷人往往会通过这些细节判断你是否有真正的工程经验而不仅仅是会背代码。3.2 链表反转考察的边界意识链表反转题我印象很深因为当时笔试给的模板代码里已经有了一部分链表节点的定义要求实现反转函数。这种题在LeetCode上很常见但笔试换了个考法不仅要写出反转逻辑还要考虑链表为空、只有一个节点、以及链表很长时是否会因为递归导致栈溢出。我的实现选择的是迭代法。反转链表的核心思想是遍历链表将当前节点的next指针指向前一个节点然后依次向后移动。这里最容易出错的地方是当你改变当前节点的next指针后原本的下一个节点就找不到了。所以在修改next指针之前必须先用一个临时变量保存下一个节点的地址。代码写完后我做了几个测试用例的推演空链表、单节点链表、两个节点的链表、多个节点的普通链表。这个习惯我一直保留到现在手写代码后在脑子里跑一遍边界用例能拦住大部分的bug。笔试的时候没有编译器测试这种脑内推演就是最后的防线。链表题里还有一种变体是要求用递归实现。递归版的反转链表逻辑上更优雅但层层嵌套的返回和指针赋值很容易把人绕晕。我个人的建议是如果笔试时间充裕且你对递归非常熟练可以用递归否则迭代版本已经足够。面试时如果面试官追问可以再补充递归的写法展示你对两种思路都理解。3.3 文件读写与内存操作的隐藏扣分点有一道综合题让从文件中读取一批整数排序后写入另一个文件。题目本身不难但这个题的隐藏扣分点很多我考后复盘时才发现自己也不算写得完美。第一点是文件打开后的错误处理。fopen返回NULL时表示打开失败原因可能是文件不存在、权限不足或者路径错误。稳妥的做法是立即检查返回值并据实际情况处理。很多笔试答案里压根没有这一步一旦文件路径出问题整个程序就直接崩溃了。这种默认文件一定存在的假设在真实项目里是相当危险的。第二点是二进制模式和文本模式的区别。Windows和Linux对\n的处理方式不同文本模式下平台会自动做换行符转换二进制模式下不会。排序整数这种数据用二进制模式读写更安全可以避免因为换行符转换导致的数据错乱。当时我用的fopen模式参数是r和w严格来说应该用rb和wb虽然当时的测试数据没出问题但后来在Linux和Windows之间交叉测试时确实遇到过因为模式选择不当导致的数据异常。第三点是文件描述符泄漏。C语言中fclose和fopen必须成对出现任何一条分支路径上漏掉了fclose都会导致文件句柄泄漏。如果在循环中反复打开文件而不关闭最终会耗尽系统的文件句柄上限。笔试时虽然没有运行环境但阅卷人看到你代码里每个return之前都有fclose就会认为你有良好的资源管理意识这属于隐藏的加分项。4. 考后复盘最容易丢分的五个地方4.1 空指针与缓冲区边界笔试结束后我第一时间把卷子里的题重新回忆了一遍因为很多题当时不确定回来再查资料、跑代码才发现当时做得并不好。事后复盘下来C语言笔试里最容易丢分的地方排在第一位的就是空指针和缓冲区边界。空指针的判断是C语言工程师的基本素养。任何指针在解引用之前都应该确认它不是NULL。但笔试时很多人为了赶时间会默认题目给的指针一定是有效的直接去解引用一旦出错就是运行时崩溃。面试官和阅卷人看你的代码时会刻意留意你有没有这个判断习惯。缓冲区边界的问题更隐蔽。比如字符串拷贝时如果目标缓冲区的大小小于源字符串的长度加一就会发生缓冲区溢出。strcpy、sprintf这类不安全的函数在笔试中出现频率很高如果你直接使用而没有检查长度就会被扣分。更好的做法是用strncpy或snprintf这类有长度限制的函数并明确传入缓冲区大小。4.2 返回局部变量地址的典型错误这一类错误我在复习时反复提醒过自己但考试时还是差点踩进去。问题出在函数内部定义了一个局部数组然后把这个数组名作为返回值返回。局部变量存储在栈上函数返回后栈空间会被回收返回的指针就成了悬垂指针指向的内存内容随时可能被覆盖。笔试里常见的变体是函数内部定义一个局部数组往里填充数据然后返回数组首地址主函数再对这个指针做读取。表面上看运行时可能碰巧能输出正确结果但这纯粹是未定义行为换个编译器优化级别或者加个函数调用结果就变了。正确做法是把目标缓冲区作为参数传入由调用方分配内存和管理生命周期或者使用malloc动态分配但那样调用方必须记得free。这个知识点在真实工作中也非常重要尤其是写库函数或者接口时内存的分配与释放必须界定清楚否则就是内存泄漏或者悬垂指针的温床。4.3 宏定义与运算符优先级的组合坑宏定义是C语言笔试里的重灾区。写一个简单的宏来求两个数的较大值很多人都能写出来但如果问这个宏的展开有什么陷阱就未必答得全了。常见的写法是#define MAX(a, b) (a) (b) ? (a) : (b)这个写法有副作用。当传入的参数是i这样的表达式时如果比较结果是truei会被自增两次而且运算符优先级可能引发问题因为宏只是文本替换没有函数调用的保护MAX(i 1, 0)这种表达式可能会被解析成完全不同的含义。正确的写法是把整个宏定义用括号包裹起来#define MAX(a, b) ((a) (b) ? (a) : (b))这样做依然无法根除副作用因此在C语言里更推荐使用inline函数。笔试中还有一种常见题目是让写出某个宏展开后的结果。这类题考察的是对预处理器工作原理的理解只有掌握了宏是文本替换不是函数调用这个本质才能准确推演展开后的代码。这类题没有捷径只能靠平时多写多推演。4.4 sizeof与strlen的区别sizeof和strlen是笔试高频送分题但同时也是高频丢分题。因为两者虽然长得像但一个是编译期运算符一个是运行期函数作用完全不同。sizeof用于获取类型或变量所占内存的字节数在编译时就能确定结果比如sizeof(char[10])的结果是10sizeof(int*)的结果取决于平台32位下是464位下是8。strlen则是计算字符串实际长度要遍历到\0才停止是运行期的行为。如果用strlen去计算一个没有以\0结尾的字符数组的长度会发生越界访问结果不可预期。有一个很经典的陷阱题定义char str[] hello问sizeof(str)和strlen(str)分别是多少。答案是6和5因为sizeof统计的是数组占用的总空间包含了末尾的\0而strlen只数到\0之前。如果这个点理解不到位选择题很容易选错。4.5 手写代码的可读性与严谨性笔试编程题虽然没有编译器但阅卷人阅读你的代码时可读性直接影响最终评分。我复盘时发现自己的代码虽然逻辑正确但命名和缩进不够规范变量名用的是tmp、a、b这类毫无意义的名字阅卷人读起来非常吃力。后来我总结出笔试手写代码的几个原则第一变量名要有意义pHead、pTail、idx这类名字比a、b好得多第二每个关键逻辑用注释标明思路比如这里需要保存下一个节点因为修改next后节点会丢失第三尽量将逻辑拆分到独立的辅助函数中而不是全部堆在主函数里第四宁可多写几行防御性代码也不要为了追求简洁而省略错误处理。这些习惯不只是为了笔试在任何代码评审场景中都同样重要。你写出来的代码是给别人读的尤其是面试官和面试时的沟通对象可读性和严谨性往往比所谓的炫技更能赢得认可。5. 针对C语言方向笔试的备考建议5.1 核心考点优先级根据我备考和实际考试的经验C研发工程师笔试的考点优先级大体如下表所示。这不是官方考点清单而是我在多场笔试和面试中观察到的共同规律。优先级考点出现频率备注第一梯队指针、数组、字符串、内存管理几乎必有编程题和选择题的绝对核心第二梯队链表、排序算法、二分查找高编程题常客第二梯队宏定义、运算符优先级、类型转换高选择题和改错题的常见素材第三梯队结构体、联合体、位运算、文件操作中选择题和综合题第三梯队static、const、extern、volatile中经常结合指针一起考如果你的备考时间有限优先把第一梯队的内容吃透再根据剩余时间往第二梯队和第三梯队扩展。至少指针、内存管理、字符串处理这三块必须做到能不看资料写出完整的代码。5.2 刷题之外还要练什么很多同学刷题的方式是看题看答案然后觉得我会了但一到限时手写就抓瞎。这是普遍存在的情况主要是因为看答案时是由答案引导的思维始终跟着别人的思路走没有真正独立解过一次。我自己备考时用的方法是每道题先自己动手写写完之后对着编译器跑一遍再和经典题解做对比。对比的重点不是谁的代码更短而是我的代码在边界条件、内存安全、可读性上有没有差距。这个过程比单纯刷题有价值得多因为每道题你都亲手踩过坑、补过漏洞。除了刷题我强烈建议练习限时手写代码这个动作。笔试环境和平时写代码完全不同没有IDE的自动补全没有编译器的即时报错甚至连代码缩进都要靠手动控制。你可以尝试在纸上或者纯文本编辑器里用十五到二十分钟写完一道中等难度的算法题再手动检查逻辑。刚开始会很不习惯但多练几次后手写的代码质量会有明显提升。5.3 答题策略与时间分配笔试时的时间分配非常关键。我的策略是先用五到八分钟快速浏览整张试卷对题量和难度形成整体判断然后从自己有把握的选择题开始做争取在短时间内拿到尽可能多的基础分。遇到不会的选择题先标记跳过不要在单题上纠结超过三分钟。编程题通常放在最后做但至少要留出四十分钟以上的时间。编程题的评分往往是按思路分和代码分来划分的就算代码没有完全写对只要思路清晰、关键逻辑正确也能拿到不少分。所以不要在编程题上一题卡太久如果十分钟内没有思路先跳到下一题全部做完后再回来补。考试结束前如果还有时间检查的重点不是代码有没有语法错误而是边界条件有没有处理所有路径是否正确释放了资源函数签名是否符合题目要求。这些检查项往往比语法错误更能决定你的分数段位。6. 写在最后关于这场笔试的几句实在话那次笔试最终的结果是顺利通过了但最大的收获不是拿到了面试资格而是在准备和复盘的过程中把以前囫囵吞枣的C语言知识系统性地弥补了一遍。后来我在日常开发中遇到的一些内存问题、指针问题追溯起来很多基础都在那个阶段打得比较扎实。如果你现在也在准备类似的C语言岗位笔试我的建议是不要迷信题海也不要指望押题。C语言笔试的核心逻辑从来不是考偏题怪题而是考察你对基本功的理解深度和代码习惯的严谨程度。把指针吃透把内存管理搞清楚把每个边界条件都当成必须处理而不是可能不会发生你的笔试结果不会差到哪里去。最后再分享一个小技巧笔试前一个晚上把sizeof和strlen的区别、static的三种用法、const修饰指针的四种组合、宏定义内外层括号的作用这几个高频考点自己默写一遍。这些知识点虽然基础但在压力状态下最容易犯迷糊考前过一遍能有效降低选择题的翻车概率。祝准备参加笔试的同学一切顺利。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门