秋招笔试,拼的不只是刷题量,而是这几种底层能力
又到了秋招季,后台不少读者在问C语言方向笔试题的事。翻出去年爱奇艺2019秋招C方向笔试题(A),说实话,这套题的难度定位很有意思:不算特别难,但覆盖面极广,语法细节、内存模型、算法思想、代码阅读能力全都考到了。不像很多公司只盯着LeetCode刷题量,这套题更看重你对C语言本身的理解深度。
我当年做这套题的时候,印象最深的是它不考偏题怪题,所有的考点都是日常开发一定会碰到的:字符串处理、指针操作、内存管理、常见排序、链表和简单图论。但就是这些“基础题”,反而能拉开差距——因为越基础的题,越考验你写代码的习惯和底层认知是否扎实。
这篇就当一次复盘,把这份真题里的核心考点逐个拆开,讲清楚每道题背后的设计意图、标准解法和那些“官方答案不会告诉你”的坑。不管你是正在备战秋招的应届生,还是想夯实C语言功底的在职开发,这篇都值得认真读一遍。
1. 这套C方向笔试题的整体设计与考核思路
1.1 从题目结构看爱奇艺的C岗位人才画像
爱奇艺的C方向岗位,主要集中在音视频处理、推荐系统后端、基础中间件开发这几个业务线上。这些场景对C语言的要求非常明确:内存要精准控制,性能要压榨到极致,代码要能在高并发环境下稳定跑几个月不出问题。
所以你看这套笔试题的考点分布,基本就是这四块:
- C语言语法基础(指针、数组、结构体、预处理器)
- 内存管理(malloc/free、内存泄漏、野指针)
- 数据结构与算法(字符串、链表、排序、图的最短路径)
- 代码阅读与调试能力(给你一段代码,让你找错误或者推输出)
这里有一个很多同学容易忽略的点:爱奇艺这类公司的笔试题,虽然看起来是考知识点,但本质上考的是“你在实际工程中会不会踩这些坑”。比如考你sizeof和strlen的区别,不是因为你面试时要手写这两个函数,而是因为在音视频缓冲区的处理中,搞错这两个值会导致缓冲区溢出,这可是线上事故级别的bug。
1.2 题型分布与时间分配的实战建议
根据A卷的结构来看,选择题约占40%,填空题约占20%,手写代码题约占40%。这个比例对于C方向岗位来说非常典型。
我的建议是时间分配这样安排:
- 选择题和填空题控制在25分钟以内,会就是会,不会先标记跳过
- 手写代码题留足40分钟以上,因为这类题不仅看结果,还看你的代码风格和边界处理是否完善
- 最后留5-10分钟检查,重点回看指针相关的题目,因为指针题往往是翻车重灾区
我在实际作答时还有个习惯:拿到卷子先花2分钟把所有题扫一遍,大致判断难度分布。因为C方向笔试题通常会有1-2道压轴题,比如迪杰斯特拉算法实现这类,提前预判能帮你合理分配精力,而不是在中间的某道题上死磕到时间耗尽。
2. 代码阅读与输出题:这些细节决定了你能不能拿满分
2.1 字符串逆序:看似简单,但至少有4种考法
字符串逆序输出是C方向笔试的常青树,也是热词榜上的高频考点。这道题虽然基础,但爱奇艺这类公司会从多个维度换着花样考。
先看最经典的考法——手写一个字符串逆序函数:
void reverse(char *str) { if (str == NULL) { return; } int len = strlen(str); int left = 0; int right = len - 1; while (left < right) { char tmp = str[left]; str[left] = str[right]; str[right] = tmp; left++; right--; } }这个实现是双指针交换的思路,时间复杂度O(n),空间复杂度O(1),也是标准答案。但笔试真正想考察的点在于:
需要注意strlen的调用是在函数开头就完成的。有些同学会在循环体内反复调用strlen,白白增加O(n²)的时间复杂度。这虽然不影响结果正确性,但会暴露你写代码时对性能不够敏感——在爱奇艺做音视频处理,这种性能浪费是不可接受的。
第二类考法是绕着弯考。比如给你一句英文,要求单词内部逆序但单词顺序不变(即“I love you”变成“I evol uoy”)。这种题就从纯考字符串变成了考你“两次逆序”的思维:
void reverseWords(char *s) { int len = strlen(s); reverse(s, 0, len - 1); // 先整体逆序 int start = 0; for (int i = 0; i <= len; i++) { if (s[i] == ' ' || s[i] == '\0') { reverse(s, start, i - 1); // 再对每个单词逆序 start = i + 1; } } }这个思路非常经典:两次逆序得到目标结果。笔试中如果遇到类似变形题,往这个方向想基本没错。
第三类考法是让你分析已有的逆序代码输出结果,这种题目考的是细心。比如代码里用了一个临时数组从尾部拷贝,但没在末尾加'\0',输出结果就会多出一堆乱码。这种题的坑点在于:很多同学一看是逆序,就直接在脑子里跑了一遍“正常版”逻辑,根本没注意到拷贝后少了字符串结束符。
第四类考法最有迷惑性:要求不申请额外空间,原地逆序。如果你上来就定义了一个新数组,那就直接扣分了。这种题目的考察意图是看你对“原地算法”的理解,在内存受限的嵌入式场景,这是一项核心能力。
2.2 指针与数组:爱奇艺超爱考的sizeof和strlen区别
在选择题和填空题部分,出现频率最高的就是sizeof和strlen的组合题。这道题看似基础,但每次都能筛掉一大批人。
来看一道典型原题:
char *str = "hello"; char arr[] = "hello"; char *p = arr; printf("%lu %lu\n", sizeof(str), strlen(str)); // 8 5(64位系统) printf("%lu %lu\n", sizeof(arr), strlen(arr)); // 6 5 printf("%lu %lu\n", sizeof(p), strlen(p)); // 8 5sizeof是编译期运算符,它计算的是变量本身占用的内存大小。strlen是运行时函数,它计算的是从指针位置开始,到第一个'\0'之前的字符个数。所以:
str是指针,占8个字节(64位系统),所以sizeof(str)为8arr是数组,数组长度包括末尾的'\0',所以是6个字节p是指针,同样占8个字节
这里有个容易混淆的点,在函数参数传递时,数组会退化为指针:
void func(char arr[]) { printf("%lu\n", sizeof(arr)); // 永远输出8,而不是数组长度 }这个“数组退化”特性在实际开发中经常引发问题。爱奇艺笔试特别爱考这个点,因为音视频项目里大量使用缓冲区数组,如果在新手代码里用sizeof去取数组长度,拿到的是指针大小,而不是缓冲区实际大小,轻则数据截断,重则缓冲区溢出。
关于strlen还有另一个隐藏考点,考察你是否了解它的实现。标准库的strlen实现其实不是逐字节扫描那么简单,很多版本的glibc会一次读取4个或8个字节,通过位运算判断是否存在'\0',这在处理大字符串时性能提升非常明显。笔试题如果让你优化一个统计字符串长度的函数,往这个方向答,会非常加分。
2.3 结构体对齐:填空和选择都爱出的高频原题
C方向笔试的结构体题,十有八九会涉及内存对齐。爱奇艺这套题里也出了一道。
typedef struct { char a; // 1字节 int b; // 4字节 char c; // 1字节 } Test; printf("%lu\n", sizeof(Test)); // 输出多少?正确答案是12,而不是6。因为编译器按照默认对齐规则,会把这些成员排列成这样:
a占偏移0b需要4字节对齐,所以从偏移4开始,偏移1-3是填充c占偏移8- 结构体总大小需要是最大对齐数的整数倍,这里最大对齐数是4,9不是4的倍数,所以补到12
如果换成下面这个顺序:
typedef struct { char a; char c; int b; } Test2; printf("%lu\n", sizeof(Test2)); // 输出8同样是三个成员,换一下顺序就从12字节变成8字节了。这在实际开发中是很实用的优化技巧,尤其是在定义大量结构体实例时,节省的内存非常可观。
爱奇艺的视频编解码模块里有大量结构体定义,内存又极度敏感,所以这个考点完全是从实战里提出来的,非常贴近他们的业务场景。听说当年有参加面试的同学因为在笔试中做对了这道题,在技术面时被面试官特别表扬了一句“内存布局意识不错”,这属于典型的用笔试表现给自己加分的案例。
2.4 典型代码改错题:野指针与返回值陷阱
这套笔试题的最后一道代码阅读题,给了一段有问题的函数,要求找出所有错误并修正。我记得原题大致是这样:
char *getString() { char str[] = "hello"; return str; } int main() { char *p = getString(); printf("%s\n", p); return 0; }这段代码犯了一个经典的错误——返回了栈内存的地址。str是getString函数内的局部数组,存储在栈上。函数返回后,这块内存就释放了;虽然大概率不会立刻被改写,但是一旦有其他函数调用,栈帧会被覆盖,p指向的内容就会变成乱码。这就是典型的“野指针”问题。
修正方案有两种思路:
第一种,使用静态存储区:
char *getString() { static char str[] = "hello"; return str; }第二种,在堆上分配内存:
char *getString() { char *str = (char *)malloc(6 * sizeof(char)); strcpy(str, "hello"); return str; } // 调用方负责free(p)第一种方案的坑在于静态变量是线程共享的,多线程环境下会有并发问题。第二种方案则需要调用方记得free,否则就会出现内存泄漏。
这也是爱奇艺这道题的核心考点:不光是让你找出语法错误,还要看你能不能识别出内存生命周期问题。在后端开发里,这种“返回局部变量地址”的错误如果出现在线上,调试起来非常痛苦,因为它的表现是随机性的,可能在开发环境跑得好好的,一上线就偶发崩溃。
3. 手写算法题:从冒泡排序到迪杰斯特拉的完整解题套路
3.1 冒泡排序及其优化:从基础版到进阶版
冒泡排序几乎是每套C语言笔试题都会出现的送分题。但爱奇艺这套题考得更深入一步,要求写出“优化后的冒泡排序”。优化点很明确:当某次遍历中没有发生任何交换,说明序列已经有序,可以提前退出。
void bubbleSort(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; // 没有任何交换,提前结束 } } }这个优化在最好情况(序列已有序)下能把时间复杂度从O(n²)降为O(n)。笔试时如果时间允许,还可以再提一个优化点:记录最后一次交换的位置,该位置之后的元素已经有序,下一轮只需遍历到该位置即可。
冒泡排序本身不是考点,考点在于你是否理解排序算法的时间复杂度分析。面试官更希望看到你写出代码后,能清晰地解释“为什么内层循环条件是j < n - 1 - i”以及“如何通过标记位降低最好情况的时间复杂度”。这些分析过程才是拉开差距的地方。
3.2 字符串逆序的多个变体与边界条件
字符串逆序在手写代码题里出现,光写出一个能跑的函数还不够,你必须把所有边界情况都考虑进去。主流的考察形式有三种:
第一种,不调用库函数,自己实现逆序。这种题目实际上是在考察你对字符串底层结构的理解——必须遍历到结尾找到'\0',然后往回交换。关键是你自己找末尾时不要越界。
第二种,不允许定义新数组,在原数组上直接操作。这就是双指针原地交换法。要注意的是中间位置的元素不需要动,当指针相遇或者交错时循环终止。
第三种,用递归方式实现。很多同学一看到“用递归实现字符串逆序”就懵了,但实际上递归思路非常清晰:
void reverse(char *str) { if (*str == '\0') { return; } reverse(str + 1); printf("%c", *str); }注意,这个递归版本只是逆序打印,并不是真正原地逆序存储。如果题目明确要求“就地逆序”,用递归会非常复杂且低效,这时候应该用迭代的双指针写法。
在笔试时如果遇到字符串逆序的变体题,我的习惯是:先写双指针的迭代版本,如果时间允许,再补充一个递归版本并说明二者区别。这样既保证正确性,又展示了你的思维深度。
3.3 迪杰斯特拉算法:A卷的压轴题
说实话,当我在A卷最后看到迪杰斯特拉算法题时,确实有点意外,因为这道题在互联网公司笔试里出现的频率并不算太高,更多出现在考研或竞赛题中。但爱奇艺考它也是有道理的:推荐系统和CDN调度里都有大量图论模型的影子。
这道题的要求通常是:给定一个带权有向图,用邻接矩阵存储,求从源点出发到所有其他顶点的最短路径。
void dijkstra(int graph[V][V], int src) { int dist[V]; int visited[V]; for (int i = 0; i < V; i++) { dist[i] = INT_MAX; visited[i] = 0; } dist[src] = 0; for (int count = 0; count < V - 1; count++) { int u = minDistance(dist, visited); visited[u] = 1; for (int v = 0; v < V; v++) { if (!visited[v] && graph[u][v] && dist[u] != INT_MAX && dist[u] + graph[u][v] < dist[v]) { dist[v] = dist[u] + graph[u][v]; } } } printSolution(dist); } int minDistance(int dist[], int visited[]) { int min = INT_MAX, min_index; for (int v = 0; v < V; v++) { if (!visited[v] && dist[v] <= min) { min = dist[v]; min_index = v; } } return min_index; }这道题的易错点主要有几个方面。首先是初值的设置,dist数组初始化时要填INT_MAX,源点的dist置为0。其次是找到最小距离节点后,要立刻将visited[u]置为1,防止后续轮次重复选中它。再次是松弛操作中的条件判断,不仅要检查两点是否有边相连,还要检查dist[u]是否本来就是无穷大,否则INT_MAX + graph[u][v]会溢出成负数,导致错误结果。
关于时间复杂度,基础版本的实现是O(V²),适合稠密图;如果改用邻接表加最小堆优化,可以降到O((V+E)logV)。笔试时可以先用基础版保底,然后在旁边简单写一句“使用堆优化可将复杂度降为O((V+E)logV)”,这能向面试官展示你的优化意识。
3.4 链表相关的手写题:反转链表与环检测
链表的题目在C方向笔试中出现频率也非常高。爱奇艺这套题虽然没有把链表作为压轴,但在选择题和填空题里考察了链表的基本概念,比如头插法和尾插法的区别。
不过我还是建议备考时把链表的几个经典手写题都练熟。最经典的一道是反转链表:
struct ListNode* reverseList(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; }这里的核心思路是保存当前节点的下一个节点,然后把当前节点的指针指向前一个节点。关键点在于循环内三行代码的顺序不能乱:先保存next,再反转指针,然后移动prev和curr。如果把顺序写反,链表就断了,找不回后面的节点。
另一个高频考点是检测链表中是否有环。最常见的解法是快慢指针:
int hasCycle(struct ListNode *head) { struct ListNode *slow = head; struct ListNode *fast = head; while (fast != NULL && fast->next != NULL) { slow = slow->next; fast = fast->next->next; if (slow == fast) { return 1; } } return 0; }这道题的判断技巧是:快指针每次走两步,慢指针每次走一步,如果有环,快指针一定会追上慢指针,但fast->next为NULL时说明链表没有环。关键在于循环条件里要同时判断fast和fast->next不为空,否则访问空指针会崩溃。
4. 内存管理与文件操作:C方向笔试的隐形重点
4.1 C语言内存管理:从堆栈分配到内存泄漏排查
很多同学复习C语言笔试时,把精力全放在算法题上,忽略了内存管理这块。但实际上,爱奇艺这类做基础架构的公司,对内存管理的重视程度绝不亚于对算法的重视程度。因为这直接关系到线上服务能不能稳定运行。
C语言的内存主要分几个区域:栈区由编译器自动分配释放,堆区由程序员手动分配释放。栈区空间有限,默认一般是1MB左右,所以大块数据不能放栈上,容易栈溢出;堆区空间大得多,但需要手动管理。
笔试题最常考的内存管理问题就这几类:
第一类是malloc和free的配对使用。每次malloc之后必须检查返回值是否为NULL,因为当堆空间不足时malloc会返回NULL,直接对空指针进行操作会导致程序崩溃。free之后必须把指针置为NULL,防止产生“野指针”——虽然free后释放了内存,但原指针还保留着地址,如果再次访问就属于未定义行为。
第二类是内存泄漏。所谓内存泄漏,是程序反复malloc但忘记free,导致可用内存越来越少。在笔试填空题中,最常见的考法就是给你一段循环代码,让你判断内存泄漏的位置。我的经验是:看到malloc就找有没有配对的free,看到循环里的malloc就格外警惕。
第三类是sizeof与malloc配合使用的正确姿势,即malloc(n * sizeof(int))的具体写法。有些同学会写成malloc(n * 4),这是有隐患的,因为int在不同平台上的字节数并不一样,不能假设为4。正确写法是用sizeof(int),这样代码跨平台编译时依然正确。
4.2 C语言文件读写操作:从fopen到fwrite的细节
文件操作这块,热词榜上的“C语言文件读写操作代码”说明有大量同学在搜这个问题。原因很简单:学校课程里文件操作讲得少,但笔试和面试时却经常被考到。
最常考的是fopen、fread、fwrite、fprintf、fscanf这一组函数的使用,以及文本文件和二进制文件的区分。先看一段标准写法:
FILE *fp = fopen("data.txt", "r"); if (fp == NULL) { perror("文件打开失败"); return -1; } char buffer[256]; while (fgets(buffer, sizeof(buffer), fp) != NULL) { printf("%s", buffer); } fclose(fp);这段代码里有几个细节值得注意:
fopen的返回值必须检查。文件打开失败的原因很多,比如路径不存在、权限不足、磁盘写保护等,如果忽略这个检查直接操作,程序会崩溃。
fgets读取文本行时,会把换行符也一并读入。如果不需要换行符,需要手动去掉:
char *pos = strchr(buffer, '\n'); if (pos != NULL) { *pos = '\0'; }关于文本模式和二进制模式的区别,在Windows平台尤其要注意。二进制文件用"rb"或"wb"打开,文本文件用"r"或"w"打开。fread和fwrite主要用于二进制文件的读写,fprintf和fscanf是格式化读写,更适合文本文件。
还有一道笔试高频题:统计一个文件中有多少个字符、多少行。这题的本质是判断换行符。核心逻辑是:逐个字符读取,遇到'\n'行数加1,所有字符数累加。
int ch; long count = 0; while ((ch = fgetc(fp)) != EOF) { count++; } printf("总字符数: %ld\n", count);注意这里ch必须用int类型而不能用char,因为EOF在stdio.h中被定义为(-1),如果char类型是有符号的且能容纳-1,会导致文本模式下正常字符0xFF被误判为EOF,造成提前结束读取。
4.3 结合爱奇艺业务场景的内存优化思路
在笔试的简答题部分,爱奇艺还出了一道很有公司特色的题:给出一段视频缓存模块的伪代码,要求分析内存使用是否合理并提出优化方案。这种题,教科书上找不到标准答案,但底层逻辑并不难。
核心思路就是两点:减少不必要的内存拷贝,以及正确管理缓冲区的生命周期。比如在缓存模块中,频繁在堆上分配小块内存再释放,会造成大量内存碎片。优化方案是使用内存池技术,预先分配一块较大的连续内存,按需切分成固定大小的小块。
内存池的代码实现笔试不一定会让你完整写出,但如果你能说出“用空闲链表管理内存块”这个思路,就能有效区分你和那些只会刷选择题的同学。
还有一点,是对存储大型结构体时减少内存浪费。如果你定义了一个结构体,里面包含了多个char数组,但不一定每个都填满,在内存紧张的环境下,可以考虑改用指针加动态分配的方式。代价是每次访问多了一次解引用操作,属于典型的以时间换空间的取舍。
5. 常见易错点与笔试复盘清单
5.1 高频易错点逐项拆解
结合热词榜和真题,我把C方向笔试的高频易错点整理成了一张清单,每条都对应一个真实丢分场景。
第一,while循环和do-while循环的区别。while先判断后执行,循环体可能一次都不执行;do-while先执行后判断,循环体至少执行一次。笔试中通常结合“输入校验”“菜单重绘”这类场景来考。比如要让用户输入一个1-100之间的数字,不合法就重新输入,用do-while就非常合适。
第二,==和=的混淆。在C语言中,if (x = 1)是合法代码,但含义是“把1赋给x,然后判断x是否为真”——永远为真。很多选择题的陷阱就设计在这里。我的习惯是把常量写在左边:if (1 == x),这样如果少写一个等号变成if (1 = x),编译器会直接报错,从根源上避免踩坑。
第三,数组越界访问。C语言本身不检查数组越界,越界访问是一种未定义行为。笔试题会通过给一段越界代码让你推断结果的方式,考察你是否意识到这一点。比如:
int arr[5] = {1, 2, 3, 4, 5}; for (int i = 0; i <= 5; i++) { printf("%d ", arr[i]); }这段代码最后会多输出一个arr[5],这是紧挨着数组的一块栈内存,内容不确定。笔试中如果问你输出结果,只能回答“未定义行为,最后一个值不确定”。能答出这一句,说明你是真的理解C语言的内存模型,而不是死记硬背。
第四,全局变量和局部变量的默认初始化。全局变量和静态局部变量由编译期自动初始化为0,普通局部变量不会自动初始化,其值是不确定的。这个点在填空题里经常出现,很多同学想当然以为所有变量默认都是0,结果丢分。
第五,break和continue的作用范围。这两个关键字在有嵌套循环时需要格外小心。break只能跳出最内层循环,如果有多层嵌套,需要借助标志变量或者goto语句跳出外层循环。continue则是结束本次循环,进入下一次迭代。选择题常考它们的区别,即使基础很好的同学,在嵌套循环的场景下也有可能选错答案。
5.2 从IDE到Linux:环境与工具链准备要点
备考期间把自己的开发环境弄清晰,是一件很容易被忽略却非常重要的事情。很多同学平时在VS Code里编译运行都没问题,一到笔试环境(通常是牛客网或者赛码网)就各种不适应,原因就是平时依赖了IDE自动完成的很多功能。
先说VS Code配置C/C++环境,热词榜里有人搜这个,说明配置过程确实容易踩坑。核心步骤其实就三步:安装C/C++扩展、配置编译器路径(Windows是MinGW的gcc.exe,macOS/Linux是系统自带或安装的gcc)、配置launch.json和tasks.json用于调试和构建。
其中最容易出问题的是Windows下配置minGW时路径写错,或者没有把编译器所在目录加入环境变量的PATH。我建议配置好之后先用命令行验证一下能不能直接执行gcc --version,如果命令行可以了,VS Code里就不会有问题。
再说在线笔试环境。牛客网和赛码网用的编译器通常是Linux环境下的GCC,默认是C11标准。这意味着:
- 本地Windows的某些写法(比如
_s后缀的安全函数)在Linux下会编译失败 int类型在64位Linux下仍然是4字节,但long是8字节,和Windows不同- 在线OJ通常不会提供完整代码,只给一个核心函数让你填写
所以我的建议是:备考代码题时,尽量在命令行下编译运行,别太依赖IDE自动补全和智能提示。平时刷题时也模拟“不看提示”的状态来写代码,这样到笔试环境就不会手生。
5.3 笔试后的复盘:把一套真题榨干
笔试结束后,很多人就彻底放松了,等着结果。但我的建议是,笔试后立刻趁热打铁重新盘一遍题目,因为这时候你对题目的记忆还热乎着,复盘效率最高。
复盘要做什么?不是把题再做一遍对个答案就行。我一般分三步:
第一步,把每道题涉及的考点列出来,看哪些题考的是同一个知识点。比如选择题里考sizeof的那道和填空题里考strlen的那道,本质上是一个考点。这样归并之后,你就会发现自己真正的薄弱点在哪里,而不是被一道一道孤立的题牵着走。
第二步,把做错的题和蒙对的题单独拿出来,写出正确解法,并想清楚自己错在哪个环节。是概念不清,还是边界条件考虑不周?建议用一个错题本或者一个mapping表把问题记录下来。
第三步,如果这套题里有你当场没做出来的算法题,一定要在笔试结束后把它完整实现一遍,并跑通测试用例。迪杰斯特拉、字符串逆序、链表反转,这些题当场没写出来不代表你不行,但如果你回去还不练,那就永远是短板了。
我自己当年笔试完的复盘习惯是:当天晚上重新梳理、整理错题,第二天尝试不看参考代码独立默写一遍。如果发现默写还是有卡壳,说明这个知识点还没真正内化,我会隔一天再重写一次。这样做几次之后,很多考点的代码就变成了身体记忆,真正面试时能做到“随手就写”的程度。
6. 最后再分享几点关于C语言备考的体会
从爱奇艺的这套真题来看,C方向笔试并不是一个“刷题越多分越高”的考试,它更像是一个“基本功+工程敏感度”的综合检验。如果你花大量时间刷各种冷门题,不如先把内存管理、指针、字符串、链表这些核心内容搞到滚瓜烂熟再来谈进阶。
有几件事是当年我自己备考时踩过的坑,这里换个角度提一下。一个是不要只背答案不看原理,一道选择题的答案是C,你记住了,下次出个变体题你还是不会。另一个是手写代码不要只在纸上写,一定要编译运行,很多隐蔽的错误(比如返回值类型不对、边界条件漏判)只有真正跑起来才会暴露。
还有一点是对待错题的态度。很多同学做完一套题就扔了,从不回看错题,这是性价比很低的做法。一套秋招真题,如果你能把它考到的所有知识点都吃透,并且能举一反三写出变形题的解法,那它带给你的价值远超随便刷十道LeetCode。
关于C语言的学习资源,如果还在打基础阶段,翁恺老师的C语言课程是很多人推荐过的入门选择,配套的练习题也值得认真刷。等你觉得基础差不多了,再找各种公司的历年笔试真题来练手,会更有针对性。但一定要记得:看课和看题都不等于会写,真正提升你代码能力的,永远是动手写到编辑器里、让它跑起来、然后看它有没有按你预期运作的过程。