C语言基础笔试核心考点:指针、内存与字符串实战解析 欢聚时代2017校招笔试题目C基础类A卷这份卷子放到今天来看依然很有参考价值。C语言基础考察的是求职者的基本功底指针、内存、字符串、结构体这些点看着都是课本内容但真要动笔写对、写完整能筛掉不少人。这篇文章我按照当年的题型和考察逻辑把C基础类笔试常考的知识点、典型题目解法、以及笔试现场的答题策略完整梳理一遍给正在准备校招或者想补C语言功底的读者一份可直接参考的复习路线。1. 先聊这份试卷的背景和整体定位1.1 欢聚时代当年为什么这么考欢聚时代就是YY的母公司当年核心业务围绕YY语音、直播、游戏相关产品展开。这类业务对客户端的实时性要求很高音视频传输、消息处理、性能优化这些场景C/C是绕不开的主力语言。所以校招笔试里专门设置C基础类试卷目标非常明确考察候选人有没有扎实的底层基本功能不能直接上手做客户端或者服务端的开发工作。这份A卷的定位是基础类意味着不会考太偏的算法题或者复杂的系统设计重点放在C语言本身的语言特性、内存模型、常见编程陷阱再加上一两道简单的算法或编程题。面试官想看的是你对指针的理解是不是停留在会用还是真正理解它背后的内存语义你写出的代码边界条件处理得是否干净你在限定时间内能不能把思路转化成可运行的代码。其实这种考察思路到今天都没变。很多过了笔试的人面试时反而在指针、内存的问题上翻车就是因为基础不牢。欢聚时代这套卷子的价值在于它代表了一批互联网公司对C基础的真实要求语法之外更看重你对内存和运行时模型的理解。1.2 C基础类笔试试卷通常长什么样根据我对同类校招笔试的观察C基础类A卷的题型结构大致如下题型题量考察重点建议用时单项选择题10-15题语法细节、运算符优先级、数据类型、关键字15分钟填空题或程序输出题5-8题指针运算、数组、字符串函数、结构体20分钟简答题2-3题关键字作用、内存分区、编译链接过程15分钟编程题2-3题字符串处理、排序、链表操作、文件读写40分钟选择题和输出题占了相当比例这类题目的特点是不需要你写完整程序但非常考验对C语言运行机制的精确掌握。比如给你一段代码让你写出输出结果题目里故意掺杂运算符的前置后置、指针的类型转换、函数参数的求值顺序这些坑平时不细心的话很容易被绕进去。编程题则是拉开差距的关键。一般会有一道字符串相关题目一道数据结构相关题目偶尔会加一道文件操作。字符串逆序、冒泡排序、链表反转可以说是C基础笔试的三大常客我在后面会逐一还原它们的标准解法和考察目的。2. 高频考点逐个拆解哪些分必须拿2.1 指针与数组笔试第一拦路虎指针和数组在C基础笔试里所占的分值通常最高。首先要理清一个核心概念数组名在大多数表达式中会退化为指向首元素的指针但在sizeof和取地址操作符中不会退化。这个知识点几乎每年都会考到形式可能是int a[10]; 问sizeof(a)和sizeof(a[0])分别是多少也可能让你比较一个指针和数组在参数传递时的区别。举个典型的笔试例子int a[5] {1, 2, 3, 4, 5}; int *p a; printf(%d\n, *(p)); printf(%d\n, *p); printf(%d\n, (*p));这里考察的是指针运算和自增运算符的优先级组合。(p)是先取p指向的值作为表达式结果然后p再后移p因为优先级高于实际等价于(p)(*p)则是把p指向的值加1。很多人第一遍做这类题时容易把p的位置和值的变化搞混。我的建议是动笔之前先画一个内存示意图把指针指向的位置标记清楚再逐条执行。另外一个高频考法是指针和数组作为函数参数的区别。数组传参时退化为指针所以sizeof接收的形参拿到的是指针大小而不是数组大小。很多笔试问为什么在函数里用sizeof(arr)/sizeof(arr[0])算不出数组长度答案就在这里。掌握这个点不仅为了答题更是为了在实际开发中避免写出有问题的代码。2.2 字符串处理笔试里的送分题与送命题字符串在C语言里本质是字符数组以\0结尾这个特性决定了它既是送分题也是送命题。送分是因为字符串相关函数大家都熟悉strlen、strcpy、strcmp、strcat几乎必考送命是因为缓冲区溢出、越界访问、忘记结尾符这些问题笔试代码里一抓一大把。典型题目是让你写出strlen的实现并分析为什么不能用while(*s)直接返回指针差值。标准实现一般是size_t my_strlen(const char *s) { const char *p s; while (*p ! \0) { p; } return (size_t)(p - s); }这道题考察的点很清晰一是const修饰符是否正确使用二是遍历终止条件的判断三是返回类型应该用size_t而不是int。有些候选人会写成int这在正规笔试里会扣分因为size_t更符合语义且避免负数问题。字符串逆序则是我见过出现频率最高的编程题。后面第3章我会专门写完整的实现和变体思路。这里先强调一个很多人忽略的点逆序操作要区分是修改原字符串还是生成新字符串前者需要原地交换后者需要额外的空间。笔试时题目如果没有明确说明一定要在答题时写清楚你的假设或者处理两种场景。2.3 结构体、内存对齐与关键字修饰结构体的内存对齐是C基础笔试的另一大考点。题目通常是给一个结构体让你计算sizeof的值。比如typedef struct { char a; int b; char c; } Test;如果直接按成员类型大小相加会得到1416但实际在默认对齐规则下结果是12。原因是编译器会按最大对齐数对齐char a后面需要填充3个字节让int b对齐到4字节边界最后的char c后面还要填充3个字节让整个结构体大小是4的倍数。这个考点在开发中也很重要因为结构体布局影响内存占用和网络协议数据包的解析。深入一点笔试还可能考#pragma pack的使用。之前我处理嵌入式通信协议时就需要用#pragma pack(1)来取消对齐因为协议字段是紧密排列的。如果你能把这个实际场景写进答案面试官会认为你真的理解内存布局的意义而不是只会背规则。关键字修饰也是必考内容。static、const、volatile这三兄弟几乎是C基础笔试的标配。static修饰局部变量时改变生命周期修饰全局变量和函数时限制作用域const修饰变量时表示只读volatile告诉编译器这个变量可能被外部因素修改禁止优化掉对该变量的访问。笔试多选题里经常把它们的各种组合混在一起比如static const int到底是什么意思这需要你自己动手写代码验证光看概念会很难记住。2.4 位运算与宏定义位运算这块笔试喜欢考异或运算交换两个变量的值、用位运算判断奇偶、移位实现乘除2的幂。纯位运算交换两个变量虽然在实际工程中用得不多但笔试就是喜欢出因为能考察你对立异或特性的理解a a ^ b; b a ^ b; a a ^ b;这里的关键是任何数和自己异或等于0任何数和0异或等于本身。第一次异或后a保存了a和b的差异信息第二次异或后b恢复为原来的a第三次异或后a恢复为原来的b。不过我在实际答题时还是会提醒一句这种写法可读性差真实项目中建议直接用临时变量。宏定义则重点关注宏与函数的区别、宏参数加括号的重要性。经典题目如下#define SQUARE(x) x * x int result SQUARE(2 3);如果不加括号会得到23*2311而不是25正确的写法是#define SQUARE(x) ((x) * (x))。这道题很能反映候选人有没有被宏的副作用坑过。我在实际项目里遇到过宏参数带自增运算的悲剧所以笔试遇到宏我的第一反应永远是检查有没有括号。3. 从几类典型题目看完整解法3.1 字符串逆序输出的两种标准写法字符串逆序输出几乎是C基础笔试编程题的标配出题形式有两种一种是让你实现函数reverse(char* str)原地逆序另一种是给定字符串按逆序打印每个字符。第一种更常见。原地逆序的标准双指针写法#include stdio.h #include string.h void reverse(char *str) { if (str NULL) { return; } int left 0; int right strlen(str) - 1; while (left right) { char temp str[left]; str[left] str[right]; str[right] temp; left; right--; } } int main() { char str[] hello; reverse(str); printf(%s\n, str); return 0; }注意这里用字符串数组char str[]来初始化而不是char *str hello因为后者指向字符串字面量修改它是未定义行为运行时大概率崩溃。这个细节我在给朋友改笔试代码时经常碰到可以说是笔试现场最常见的隐性bug。如果题目是要求逆序打印但不修改原字符串那就不需要交换直接遍历到末尾再往前输出即可。有些笔试会在第二问里叠加只使用O(1)额外空间那就只能用双指针原地交换不能开新数组。3.2 冒泡排序与排序算法的边界处理冒泡排序作为最简单的排序算法笔试出现的目的是考察代码会不会写完整、边界条件对不对。标准实现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 temp arr[j]; arr[j] arr[j 1]; arr[j 1] temp; swapped 1; } } if (!swapped) { break; } } }很多人在笔试时会把内层循环写成j n - 1这也能运行但会多做不少无意义的比较。加上swapped标志位做提前退出是这题的加分项说明你考虑到了数组已经有序时的时间复杂度优化。排序算法这块笔试如果只考冒泡那属于送分有的公司会让你手写快排或者归并排序那就需要额外准备。我的建议是快排一定要能流畅写出来因为它是C语言笔试和面试中出现概率最高的排序算法冒泡和选择反而是拿来给基础薄弱的候选人保底的。3.3 单链表反转手撕代码常客链表反转在2017年前后的校招笔试里出现频率非常高因为C语言本身没有内置的链表实现考察链表操作最能反映候选人是否理解指针的本质——指针的指针、结构体嵌套、内存的动态分配。迭代法反转单链表的标准写法typedef struct Node { int data; struct Node *next; } Node; Node* reverse_list(Node *head) { Node *prev NULL; Node *curr head; while (curr ! NULL) { Node *next curr-next; // 先保存下一个节点 curr-next prev; // 反转指针方向 prev curr; // 前驱后移 curr next; // 当前节点后移 } return prev; // 最后prev就是新头节点 }这道题的灵魂是先保存后继节点这个动作。很多人写反转时改了curr-next之后发现没法继续遍历了就是因为没有先用临时变量保存next。这个细节在笔试时特别值得注意因为一旦理解错误整个while循环的逻辑都是错的。如果笔试时间充裕可以额外补充递归写法。递归写法的核心是先假设子问题已经反转完成再处理当前节点的指针。虽然递归写法更简洁但笔试时我更推荐迭代法因为递归容易在边界条件上出错而且面试官追问递归的空间复杂度时你还需要答出O(n)的栈空间。3.4 文件读写操作的完整套路C语言的文件操作也是C基础笔试常客特别是读写文本文件、统计字符数、复制文件这类题目。2017年那会儿很多公司的笔试环境仍然允许本地编译所以文件题会比较多的出现到了现在在线OJ盛行的时代文件题的比例下降了但理解标准套路仍然有价值。文件读取并统计行数的完整示例#include stdio.h int main() { FILE *fp fopen(input.txt, r); if (fp NULL) { printf(open file failed\n); return 1; } char buffer[1024]; int line_count 0; while (fgets(buffer, sizeof(buffer), fp) ! NULL) { line_count; } fclose(fp); printf(total lines: %d\n, line_count); return 0; }这段代码有几个必须记住的点打开文件后一定要检查返回值这个习惯能避免空指针崩溃读取用fgets而不是gets因为gets不检查缓冲区大小非常不安全最后一定要fclose释放资源。笔试时如果题目要求写入文件同样要注意fopen的打开模式写文本用w追加用a。我看过不少笔试代码候选人的算法逻辑没问题但fopen之后不检查空指针或者fclose之前就return导致资源泄漏。这些基本都是实际写代码时会犯的错误面试官从这些小细节里能看出你平时的工程习惯如何。4. 笔试现场的答题策略与时间分配4.1 选择题怎么用排除法拿分选择题部分C基础类卷子通常会有几道题故意设置看起来都对但只有一个完全正确的选项。我的策略是先看题目问的是正确的是还是不正确的是圈出关键词再逐项分析。特别是多选题少选可能得分选错可能扣分这时候不确定的选项宁可不选也不要乱填。还有一个实战技巧对于运算符优先级的题目不要凭记忆硬做可以采用加括号拆分的方式。比如问你*p等价于什么你就在心里把它拆成*(p)然后判断优先级的优先级高于*所以先自增但因为是后置所以表达式的值是自增前p指向的值。这种方法比死记优先级表靠谱得多。如果遇到真的完全不会的选择题先标记跳过等做完所有题之后再回来蒙一个。绝对不要在选择题上死磕超过两分钟C基础类卷子的分值大头在后面的编程题时间要留给真正能拉开差距的地方。4.2 编程题怎么写才能让面试官愿意看编程题的评分通常不会只看结果对不对面试官更在意你的思维过程和代码风格。C基础类笔试的编程题我建议按下面这个顺序来写先写注释说明解题思路。用一两句话写下我打算用双指针从两端向中间扫描之类的思路就算代码没写完面试官也能看出你有清晰的解题方向。边界条件优先处理。判断输入是否为空指针、数组长度是否为0这些放在函数开头。核心逻辑分段写清楚。不要把所有逻辑挤在一堆用一个空行隔开关键步骤加上简短注释。最后写测试用例思路。如果时间允许在代码下方注明你想用什么用例来验证比如输入空字符串、单个字符、奇偶长度等。代码风格方面变量命名一定要有意义不要用a、b、c这种无意义命名。笔试时我自己习惯用left、right、prev、curr这种能表达语义的名称因为面试环节面试官很可能会拿着你的笔试代码来问问题命名清晰的代码会给你省掉很多解释成本。4.3 在线笔试环境下的注意事项2017年那会儿很多公司还处于线下笔试和在线笔试并存的阶段现在的校招则基本都是在线笔试了。在线笔试的环境和本地编译器有一些区别这里提醒几个容易踩的坑。第一务必确认输入输出格式。在线OJ以标准输入和标准输出作为交互方式笔试题目里通常会给出输入格式和输出格式的描述。很多人代码逻辑完全正确但多打印了一个提示语导致格式不匹配被判0分。如果你不确定建议先看示例输入输出完全模拟它的格式来写。第二注意多组数据的循环输入。很多C题目要求处理多组测试数据这时候要用while(scanf(...) ! EOF)的循环结构而不是只处理一次。这道题在历年校招里反复出现如果你没养成这个习惯非常容易失分。第三检查编译错误要细心。在线笔试的编译环境一般不会给出很友好的错误提示常见的就是变量未声明、类型不匹配、缺少头文件。我习惯在写完代码后自己扫一遍include再检查有没有临时变量使用了中文输入法导致的错误符号。这些小坑虽然低级但在紧张状态下就是容易犯。5. 从笔试到面试复盘中的经验与问题排查5.1 常见错误清单与自查方法我把C基础类笔试里最容易出现的错误整理成一张速查表方便你在模拟练习时逐项自查错误类型具体表现自查方法指针悬空对空指针解引用程序崩溃检查函数入口是否有空指针判断数组越界循环访问超出数组长度检查循环边界尤其注意n-1是否出错缓冲区溢出strcpy/gets写入超出目标缓冲区改用strncpy传入缓冲区长度忘记结尾符字符数组没有\0就输出初始化时使用char str[] ... 而非手动逐个赋值内存泄漏malloc后没有free检查每个malloc是否配对free返回值错误该返回0表示成功返回了非0确认函数语义和调用方的判断逻辑一致我在帮人复盘笔试代码时发现一个规律80%的C语言bug集中在内存操作上面真正算法逻辑出错的反而不多。所以自查时优先检查指针、数组、字符串、动态内存相关的代码这样效率最高。5.2 准备工作中的信息差问题准备C基础笔试时很多人的误区是去背各种题库但实际上笔试考察的不是你会不会背题而是你能否在限定时间内把最基本的C语法和编程思想用对。根据我的经验准备这类笔试最有效的方式是找一个Linux环境或者本地编译器把经典题型全部手写一遍。光看代码、背代码和亲手敲一遍的效果完全不同。比如strcpy的进阶版本、链表的插入删除、二叉树的遍历这些题型你至少要在编辑器里独立实现过三遍才算真正掌握。我在面试别人时发现很多人笔试能写出代码但被问到这段代码的内存布局是什么样就答不上来说明他只是背下来了并没有真的理解。所以准备时一定要问自己三个问题这段代码为什么这么写它会在内存里产生什么效果如果输入变了哪个地方会崩溃这三个问题想清楚了笔试和面试都能过。5.3 后续扩展建议C基础类试卷通过之后一般还会有一轮面试。面试官会拿着你的笔试卷子或者代码让你现场讲解所以笔试结束后一定要趁热打铁把自己写的代码重新看一遍想清楚每一步的目的以及面试官可能追问的优化点。如果笔试代码里有不完善的地方但你自己能看出来面试时可以直接说我当时的思路是XXX但我认为这里还可以优化成XXX这反而会加印象分因为面试官看重的是你能意识到问题和改进方向而不是伪装自己完美无缺。最后分享一点个人的体会欢聚时代这份2017年C基础类A卷放到现在看考点并没有过时。指针、数组、字符串、链表、文件操作这些内容永远是C语言的核心也是客户端和底层开发岗位招聘的必考项。我见过很多候选人在简历里写熟悉C语言但一写代码就暴露了对内存布局、边界条件、字符串安全的认知缺失。其实这种基础类的笔试恰恰是筛选真熟悉和假熟悉最有效的手段。准备这类笔试我的建议很直白别刷偏题怪题把最基础的函数实现、常用算法、内存操作吃透手写代码的能力练出来比看一百篇面经都管用。如果你能把本文提到的这些考点的代码都独立写一遍再对照检查边界条件和资源管理我相信通过这类C基础笔试的把握会大很多。