尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

金山办公C++校招笔试题复盘:从C++基础到文件读写的常见考点

金山办公C++校招笔试题复盘:从C++基础到文件读写的常见考点 如果你正准备金山办公2020校招C开发工程师笔试题一我强烈建议先把这篇复盘看完。2020年秋招的时候我网申了这个岗位收到笔试链接后特意把WPS客户端翻出来用了一遍心想做办公软件的公司笔试大概会考文档结构、UI框架、插件机制这类东西。结果点开题目才发现它考得远比我预想的要细细到每一道选择题都会在选项里埋两三个坑。这篇文章基于我当年参加笔试的回忆以及同期校招题库里高频出现的同类题目整理而成不是官方原卷但考点、雷区和解题思路都是实打实的。整体适合正在准备C校招笔试的人阅读尤其建议目标锁定金山办公、WPS或者同类办公软件大厂的候选人好好看一遍——搞清楚它考什么、为什么这样考比盲目刷两百道题更有用。1. 这套卷子的整体风格与核心考点分布先说结论金山办公的这套笔试题一给我的整体印象是题量不大但覆盖面非常广。90分钟里印象中有20道左右的选择题、2道简答题和2道编程题。选择题占大头但编程题的分值很高而且一旦编译不过或者逻辑有边界问题基本就是整道题丢分所以不能只盯着选择题复习。我根据回忆把考点分布整理成了下面的表格比例不一定精确但大致反映这套题的重心考试模块典型考点出题比例印象C语言基础指针/引用、const、内存布局、虚函数30%数据结构与算法字符串处理、链表、排序25%操作系统进程线程、死锁、内存管理15%网络与LinuxTCP/UDP、HTTP、常用命令10%文件与I/OC标准库、文件指针、读写模式10%其他编译链接、内存泄漏、调试技巧10%为什么是这样一个分布办公软件客户端的特点是长期运行、要处理大文档、对崩溃率要求极高所以笔试会重点看候选人能不能驾驭指针和内存管理。C基础题占比最高恰恰说明这家公司对基本功的重视。另外WPS涉及大量文件格式解析和写入文件I/O题目几乎是必考。Linux方面客户端开发虽然主要跑在Windows上但服务端和自动化构建体系普遍是Linux所以基础命令和TCP/IP概念也会带一些。如果按10分制给这套题打分我认为难度在6.5分左右。它不会让你手写红黑树但会在一个看似简单的指针选择题里藏两三个坑。我印象最深的一道题是问“数组名和指针的关系”考array 1到底跳了多远。这种题在牛客网和力扣讨论区里都是常客可当时依然有一批候选人栽在上面。后面我会专门展开讲。2. C基础题那些“一看就会一写就错”的语法细节C基础题的关键不是记住结论而是理解结论背后的原理。笔试里最常见的手法就是把多个“看起来差不多”的概念放进同一个选项让你挑错。如果你只背结论很容易被干扰项带走。2.1 引用与指针第一道经典陷阱题目大致是这样的下列关于引用的说法正确的是A. 引用一旦初始化就不能再绑定其他对象B. 引用可以为空C.sizeof(引用)返回的是引用变量本身的大小D. 引用变量一定不占用内存答案是A。引用在语义上是对象的别名初始化时必须绑定一个对象之后不能再换绑。B错在“空引用”这个概念不成立引用没有独立于对象的地址也不会指向空。C是最容易错的一项很多人以为sizeof一个引用就是引用本身占的字节数实际上sizeof(引用)返回的是被引用对象的大小。D也错C标准没有规定引用变量本身是否占用内存具体看编译器实现所以绝对不能选“一定不占用”。这个点看起来基础但笔试里正确率并不高。我后来总结了一个记忆方法把引用想成“对象的第二个名字”所有对引用的操作都会作用到原对象上。传参时如果函数内需要修改实参且实参不可能为空优先用引用如果需要处理“空对象”这类情况再考虑指针。2.2 const、指针和数组名绕不开的组合技第二类高频题是const和指针的组合。比如声明含义const char *p不能通过p修改指向的值但p可以指向其他对象char const *p与上一条相同const修饰的是charchar *const pp是常量指针p不能再指向别处但可以修改指向的值const char *const p指向的对象和指针本身都不可修改这种题真的没什么技术含量但每次笔试都有大量人错。我建议不要死背而是从右往左读比如const char *p从p开始往左读p是一个pointer to const char所以p可变指向的内容不可变。char *const p则是const pointer to char所以p不可变指向的内容可变。还有一个必考变体就是数组名和指针的区别。看这段代码#include stdio.h #include string.h int main() { char str[] hello; char *p str; printf(%zu %zu\n, sizeof(str), strlen(str)); printf(%zu %zu\n, sizeof(p), strlen(p)); return 0; }在64位机器上输出通常是6 5和8 5。原因在于数组名在sizeof中不会退化成指针sizeof(str)拿到的是整个数组的大小也就是6字节包含结尾的\0而sizeof(p)拿到的是指针变量的大小和平台位数有关。strlen只统计字符不算\0。旁边再补一个变种如果数组作为函数参数比如void foo(char a[])在函数内部sizeof(a)永远是指针大小因为数组参数会退化为指针。这是C/C里最容易踩的坑没有之一。2.3 虚函数与构造函数藏在“多态”里的细节再看一道常见选择题在构造函数中调用虚函数会发生什么A. 调用当前类的虚函数不会发生多态B. 会发生多态调用派生类的实现C. 编译错误D. 未定义行为答案是A。原因是构造派生类对象时基类构造函数先执行此时虚表指针指向的是基类的虚表而不是派生类的虚表。所以在构造函数里调用虚函数实际执行的是当前正在构造的这个类的版本不是最终派生类的版本。我见过不少同学对这个结论非常吃惊因为在普通函数里调用虚函数确实是多态的但构造函数不是普通场景。这套题把它设计成选择题就是想考察你有没有真正理解虚函数机制。另外一个高频衍生题是析构函数为什么通常要声明为virtual答案是当通过基类指针删除一个派生类对象时如果析构函数不是虚函数就只会调用基类的析构函数派生类资源不会被正确释放产生未定义行为。笔试里这个知识点几乎年年出现值得多花几分钟想透。3. 编程题实战字符串逆序与指针操作的攻防要点这套题一里的编程题第一道经典到不能再经典给定一个字符串要求原地逆序输出不允许额外分配和字符串等长的数组。输入输出都通过标准输入输出完成。题目看着简单但真正写起来会暴露不少细节问题。3.1 双指针法最稳妥的写法我当时的写法是双指针先给出完整代码#include stdio.h #include string.h void reverse(char *s) { if (!s) return; int left 0; int right (int)strlen(s) - 1; while (left right) { char tmp s[left]; s[left] s[right]; s[right] tmp; left; right--; } } int main() { char buf[1024]; while (scanf(%s, buf) ! EOF) { reverse(buf); printf(%s\n, buf); } return 0; }这里有一个很多人第一次写都会犯的错right strlen(s) - 1。如果字符串为空strlen(s)返回00减1在无符号类型下会变成一个非常大的数导致循环条件判断异常。我把left和right定义成int就是为了避免无符号整型下溢。如果你定义成size_t就必须写int right (int)strlen(s) - 1;或者单独判断空串。另一个细节是scanf(%s, buf)只能读到空格前的字符串如果题目要求反整个句子比如“hello world”变成“dlrow olleh”scanf就不够用。在线笔试里更稳妥的是用fgets读取整行但要注意fgets会把末尾换行符也读进来处理时需要去掉。3.2 递归版能写但要慎用除了双指针还可以用递归实现字符串逆序void reverse_recursive(char *s, int left, int right) { if (left right) return; char tmp s[left]; s[left] s[right]; s[right] tmp; reverse_recursive(s, left 1, right - 1); }递归的优点是代码简洁缺点也很明显递归深度等于字符串长度的一半字符串很长的时候可能导致栈溢出。笔试环境通常限制栈大小所以除非题目明确要求用递归否则我更推荐双指针。万一考官追问“有没有其他实现”再把递归版写出来会显得你理解更全面。3.3 进阶变形按单词反转句子这道题在金山办公一期里没有直接出现但和字符串逆序同源属于“换一层皮”的常考题。比如输入“hello world”要求输出“world hello”。思路非常巧妙先把整个字符串反转成“dlrow olleh”然后把每个单词内部再反转一次就得到“world hello”。void reverse_range(char *s, int left, int right) { while (left right) { char tmp s[left]; s[left] s[right]; s[right] tmp; left; right--; } } void reverse_sentence(char *s) { int n (int)strlen(s); reverse_range(s, 0, n - 1); int i 0; while (i n) { while (i n s[i] ) i; int start i; while (i n s[i] ! ) i; reverse_range(s, start, i - 1); } }这段代码要注意连续空格的处理。循环里先跳过空格找到单词起点再一直扫到空格或结尾然后反转这个区间。整体反转和局部反转用的是同一个辅助函数逻辑很干净。笔试里如果时间紧张可以先用注释把思路写清楚再补代码这样就算没有完全调通阅卷人也能看到你的解题方向。3.4 顺带一提排序题的“保命模板”笔试编程题偶尔也会让写排序算法尤其是冒泡排序和快速排序。冒泡排序本身不难但很多人会在边界条件上翻车。我给一个加了优化标志位的版本void bubble_sort(int arr[], int n) { for (int i 0; i n - 1; i) { int swapped 0; 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; swapped 1; } } if (!swapped) break; } }这个swapped标志位可以在数组已经有序时提前退出循环最好情况时间复杂度降到O(n)。面试官如果追问“还能怎么优化”这就算一个小亮点。不过笔试编程题里真正高频的还是字符串和链表排序反而出现频率低一些别把它当成大头。4. C语言文件读写校招笔试里容易被忽视的送分题很多人觉得文件读写是学校大作业才会用到的知识但在金山办公的笔试里这是一道妥妥的送分题同时也是拉分题。为什么因为不常写的人一上来就会踩feof和文件打开模式的坑。4.1 统计字符出现次数fgetc的返回类型是int先看一道典型题统计文本文件里字符a出现的次数。#include stdio.h int main() { FILE *fp fopen(data.txt, r); if (!fp) { perror(fopen); return -1; } int count 0; int ch; while ((ch fgetc(fp)) ! EOF) { if (ch a) count; } fclose(fp); printf(%d\n, count); return 0; }重点在于fgetc返回的是int而不是char。因为文件结束标志EOF通常是-1如果把返回值定义成char在一些平台下char是无符号类型永远不可能等于-1循环会变成死循环。这个细节几乎每次都会刷掉一批人。我当时在本地测试的时候把ch定义成了char程序运行得倒是“正常”因为本地编译器把char当成有符号类型处理。结果到了在线笔试环境编译参数变了行为就可能完全不同。所以从写代码的第一秒就要养成用int接收fgetc返回值的习惯。4.2 二进制文件复制fread的返回值是元素个数再看第二道常见的文件操作题将一个二进制文件完整复制到另一个文件。#include stdio.h int main() { FILE *src fopen(in.bin, rb); FILE *dst fopen(out.bin, wb); if (!src || !dst) { perror(fopen); return -1; } char buf[4096]; size_t n; while ((n fread(buf, 1, sizeof(buf), src)) 0) { fwrite(buf, 1, n, dst); } fclose(src); fclose(dst); return 0; }这里有两个容易踩的坑。第一打开二进制文件必须用rb和wb不能用r和w。在Windows环境下文本模式会把\r\n自动转换成\n二进制文件会被改坏。第二fread的返回值表示“成功读取的元素个数”不是读取的字节数。虽然每次读取单个字节时二者相等但如果你写fread(buf, sizeof(buf), 1, src)返回的就不是字节数了所以要灵活看待fread的参数。4.3 feof的经典误用先读后判断而不是先判断后读feof也是笔试的常客。很多人会写成这样while (!feof(fp)) { fgets(buf, sizeof(buf), fp); // 处理 buf }这个写法的问题在于feof只有在读取操作触发EOF后才会置位所以在读到文件末尾的最后一行时feof可能仍然是假循环会多进入一次而这次fgets返回的是NULLbuf里的内容其实已经失效了。正确的做法是先读取再判断返回值是否为NULLwhile (fgets(buf, sizeof(buf), fp) ! NULL) { // 处理 buf }类似的道理也适用于fread、fscanf等函数。笔试中如果不确定可以记一句口诀读取操作本身才是文件结束的信号feof只是考试前检查用的那面镜子不能作为行驶方向的依据。4.4 路径与资源在线笔试最容易丢分的地方最后一条经验更具实战意义在线笔试系统里当前工作目录往往和本地IDE完全不一样。题目如果说“读取data.txt”你最好不要自作聪明地写绝对路径“C:\Users\xxx\data.txt”也不要想当然地用相对路径。最稳妥的做法是照抄题目给的路径并额外判断文件是否打开成功。还有一点我当年吃过亏写完文件操作后忘了fclose。本地跑一次两次没问题但在线评判系统可能同时打开大量文件文件描述符耗尽就会报错。所以写完文件操作后先在代码里把资源释放逻辑补上尤其是分支返回的地方别让文件句柄漏掉。5. 时间分配与做题顺序别在选择题上耗死自己前面讲完知识点现在讲一个更实际的问题90分钟怎么分配才合理。我那次笔试题里有一道关于虚析构函数的选择题纠结了十分钟最后两道编程题只写完了第一道第二道只写了个大概框架。复盘的时候我发现问题不是题目太难而是我把时间押在了一道2分的题上丢掉了20分。如果你不想重蹈覆辙可以参考下面这套流程。5.1 先通读全卷再决定作战顺序很多人点开试卷就从头开始按顺序做这是最被动的方式。我建议前3分钟什么都不做只把卷子滚一遍标记出三类题目会做的、可能做对的、完全没思路的。接下来先完成优先级最高的编程题因为编程题分值高、逻辑独立写好两题后心态会稳很多。然后做简答和选择题。选择题给自己定一个硬性规则每题最多2分钟超过2分钟立刻凭第一印象选一个并做上标记。当年纠结虚析构函数的那道题如果早用这个规则后面编程题就能多出五六分钟。5.2 编程题先写思路再写代码在线笔试不像本地IDE有智能提示和实时调试一旦进入编程题页面跑测试用例的按钮往往会让人紧张。更好的顺序是先在草稿区用注释写出思路比如“1. 先反转整个字符串2. 再反转每个单词”然后再填充具体代码。这样如果后半段出现低级编译错误你还可以根据注释快速定位逻辑而不是看着一段代码发呆。最后留出10分钟检查重点检查三件事输入输出是否完全匹配题目要求、边界条件是否处理了空数组或空字符串、所有变量名是否统一。尤其是scanf和printf里的格式占位符一个%d写错就能让整个程序的输出变成乱码。5.3 选择题的“极限举例法”这里分享一个做C/C选择题的技巧遇到拿不准的语法结论可以取一个极限例子放进大脑里模拟比如让指针为NULL或者构造一个长度为0的数组。很多选项描述得看起来很对但只要带一个边界值进去就会发现它漏洞百出。举个例子题目问“sizeof一个空类是多少”很多人背过结论是1但如果理解不深题目换个说法问“空类对象地址有什么特点”你可能就慌了。用极限举例法去想C禁止对象地址完全相同所以编译器会为每个对象分配至少一个字节的占位这样sizeof空类结果才是1。把这种思维方式练熟比死记题库更管用。6. 复盘与延伸这套题给后续校招的启示把整套题一走完一遍你会发现金山办公的笔试有个很清晰的逻辑C基础证明你懂这门语言数据结构和算法证明你能写逻辑文件操作证明你能处理真实业务时间管理能力则说明你是个能在压力下工作的人。考点本身并不偏难的是在有限时间内不犯错。如果你接下来要参加类似的C开发岗笔试我建议从这几个方向做准备基础题吃透《C Primer》前几章和《C程序设计语言》里指针、数组、字符串、文件相关的内容。这已经不是“背完就能过”的层面而是要求你把每个结论的原理讲清楚。算法题每天限时写两道经典题重点练字符串、链表、二叉树和排序。不要只看题解要真的动手敲跑测试用例观察边界行为。模拟笔试在牛客网或者力扣的在线编程环境里按考试时间做整套卷子别只在自己的本地IDE里刷。这样能提前适应在线评判系统和没有代码补全的“裸写”状态。错题本每次刷题后把错误选项对应的知识点记下来。比如“fgetc返回值类型”“feof使用时机”“array1的步长”考前翻一遍比自己盲目刷几十道题管用得多。我在准备这类笔试时最大的收获不是背会了多少题而是学会了“对每个选择都问一个为什么”。为什么引用比指针安全为什么feof不能提前判断结束为什么二进制模式打开文件会更稳妥把这些为什么弄明白遇到没见过的新题才不会慌。如果你正打算投金山办公或者正在准备类似的C开发岗笔试希望这份基于回忆的复盘能让你少走一些弯路。笔试没有捷径但可以有更聪明的准备方式。
返回列表