网易嵌入式软件工程师笔试复盘:考点、编程题与备考路线
现在回想起来,网易那套嵌入式软件工程师的笔试算是当年秋招里比较有代表性的一套题了。我是在线笔试,统一时间开考,全程开摄像头,题型包含单选、多选、填空和两道编程题,整体风格偏基础但覆盖面很广,和很多只考算法的大厂笔试不太一样——它更像是在考察“这个人在学校到底有没有认真做过嵌入式相关的东西”。这篇就从笔试考点、编程题思路、后续面试项目和备考路线几个维度拆开聊聊,给后面准备网易或者其他大厂嵌入式岗位的同学一个参考。
1. 笔试整体情况与考察范围
1.1 考试流程与答题环境
网易2020校招笔试用的是第三方在线笔试平台,嵌入式软件工程师(正式批)这一场大约两个小时,时间不算特别宽裕。我记得进入系统之后先要做一个环境检测,确认摄像头、麦克风、屏幕共享都正常,然后才进入答题页。页面分左右结构,左边是题目列表,右边是答题区域,有题目跳转和标记功能。做题过程中会随机截屏,所以最好提前把桌面清理干净,别开着即时通讯工具,这个真会被判作弊。
题型分布大致是:单选题10道左右,多选题5道左右,填空题若干,最后是两道编程题。不过网易每年的题目结构并不完全固定,我那一场的编程题是二选一的形式,也就是两道题你可以任选一道作答,选得分预期更高的一道即可。整体难度中等偏上,拉开差距的主要不在选择题,而在后面的编程题和部分多选。
1.2 考察模块全景
把整套题过一遍之后,你会发现网易嵌入式笔试的考点高度集中在以下几个方向:
| 考察模块 | 占比(估计) | 典型考点 |
|---|---|---|
| C语言基础 | 25% | 指针、内存布局、结构体对齐、位运算 |
| 操作系统 | 20% | 进程线程、同步互斥、死锁、内存管理 |
| 数据结构与算法 | 15% | 链表、二叉树、排序、复杂度分析 |
| 嵌入式系统知识 | 20% | 中断、寄存器、通信协议、RTOS基础 |
| 计算机网络 | 10% | TCP/UDP、IP分片、socket模型 |
| Linux基础 | 10% | 常用命令、文件系统、编译链接 |
这里每个模块我都会在后面展开讲,先说说为什么是这个分布。嵌入式软件工程师在网易这类公司里,做的更多是物联网设备、智能硬件配套和底层系统相关的工作,所以对C语言和操作系统的要求特别高。同时你还需要懂硬件——至少要知道寄存器怎么操作、中断是什么、I2C和SPI的区别。这些在笔试里都会以基础题的形式出现,不深,但如果没接触过就完全答不上来。
1.3 单选题与多选题的答题策略
网易的多选题是少选得部分分、错选不得分,这个规则会在考试前说明。我当时采用了保守策略:不确定的选项宁可不选。比如有一道关于进程间通信的题,选项里有管道、消息队列、共享内存、socket,前三个我确定是进程间通信方式,第四个我犹豫了一下,但想到socket通常用于网络通信,也可以用于本地进程间通信,最后还是没有选它。结果出来之后发现socket确实也是可以的,但因为没有把握,选择不选也不亏。对于多选,拿不准的选项坚决不碰,这是大厂笔试里最实用的策略。
单选题里有一类题值得专门说,就是“找出以下代码的错误”。这类题考察的是C语言细节,比如指针未初始化就赋值、数组越界、结构体比较用等号、字符串用==比较等等。做题的时候不要着急,先在心里模拟一遍代码的执行过程,尤其注意指针指向和内存分配的情况。
2. 核心知识点深度拆解
2.1 C语言:指针、内存与结构体
C语言是整个嵌入式笔试的重头戏,网易的题从来不问你“什么是指针”,而是直接给你一段代码让你判断输出或者指出错误。我把当年遇到的高频考点整理一下:
先是指针与数组的关系。这类题最爱考sizeof和strlen的区别,比如char *p = "hello"和char arr[] = "hello"在sizeof上的差异。sizeof(p)在64位系统上是8,sizeof(arr)是6(包含结尾的\0)。这个很多人第一次做都会错,因为潜意识里觉得“都是字符串”,但数组名在这里代表整个数组,指针变量只是存了一个地址。
再就是指针的指针和多级指针。网易有一道题是函数参数传指针的经典问题:函数内部对指针做malloc,但外部指针并没有发生变化。这其实考核的是C语言的值传递特性——你传入的是一个指针变量的副本,函数内部给这个副本重新赋值,外部的原指针当然不会变。要解决这个问题,要么用二级指针,要么让函数返回新的指针。这种问题在实际工作中也很常见,很多刚毕业的同学会在封装初始化函数时栽在这里。
结构体对齐也是高频考点。有一道填空题是计算结构体大小:
struct example { char a; int b; char c; };在默认4字节对齐的编译器设置下,sizeof(struct example)是12而不是6。规则是每个成员按自身对齐数对齐,整体按最大对齐数对齐。char a占1字节,补3字节到偏移4的位置放int b,char c在偏移8的位置,由于整体对齐数为4,结构体大小取4的倍数,所以是12。如果把成员顺序调整成int b; char a; char c;,大小就变成8。这算是一个性价比极高的知识点,背下规则就能拿分。
还有一类是位运算。嵌入式场景里位运算用得太多了,寄存器操作就是典型的位掩码操作。网易考过类似“如何将一个字节的bit3置1、bit5清零”这样的题,答案分别是num |= (1 << 3)和num &= ~(1 << 5)。这类题没有难度,但要求你写代码的时候不能出现|=和&=用反的低级错误。
2.2 操作系统:进程、线程与并发
操作系统的题在笔试里占比很大,而且经常和嵌入式结合出题。比如问“在嵌入式系统中,为什么用线程比用进程更合适”,这其实就是在考察进程和线程的对比。进程是资源分配的基本单位,线程是调度的基本单位,同一个进程的多个线程共享地址空间,切换开销小。在资源受限的嵌入式设备上,线程的优势非常明显。
同步互斥是必考内容。信号量、互斥锁、条件变量这三个概念的区分,网易以多选题的形式考过:哪些机制可以用于线程间同步?答案应该是互斥锁、信号量、条件变量都可以,自旋锁也可以。这里需要特别注意,很多同学会把“互斥锁”和“二值信号量”搞混,它们确实可以实现类似的互斥效果,但信号量有计数的概念,可以用于资源数量的管理,而互斥锁强调所有者概念。在笔试的语境里,题目如果问“哪些可以用来实现互斥”,二值信号量是可以选的。
死锁的四个必要条件——互斥、持有并等待、不可剥夺、循环等待——几乎是所有大厂笔试的常客。网易考的是给出一个场景,让你判断是否可能发生死锁。比如两个线程分别持有一把锁再尝试获取对方的锁,这显然满足循环等待,会发生死锁。应对死锁的方法无非是破坏四个必要条件之一,最常见的是破坏循环等待,即对所有资源按序加锁。这个思维在后续面试手写多线程代码时也用得上。
2.3 数据结构与算法:链表、二叉树与复杂度
网易嵌入式笔试的数据结构题难度不算高,但很实用。单选题里考到了链表和数组的对比:在中间插入元素,链表时间复杂度是O(1)(前提是已经定位到插入点),数组是O(n);随机访问元素,数组是O(1),链表是O(n)。这种对比题只要理解两种结构的内存布局就能答对。
二叉树考过前序遍历和中序遍历,还有根据遍历序列还原二叉树的选择题。给定前序和中序,就能唯一确定一棵二叉树,这个知识点我建议大家在考前专门练几道手写题,因为不光笔试会出,后面面试手撕代码也经常遇到。
排序算法也出现了,考的是稳定性。比如哪些排序算法是稳定的?冒泡、插入、归并是稳定的,选择排序、快速排序、堆排序不稳定。这种题属于细节点,很多人复习的时候容易忽略,但确实考了。我建议把常见排序算法的时间复杂度、空间复杂度、稳定性整理成一张表,考前花半小时过一遍。
关于算法题,网易嵌入式岗位的笔试编程题难度远低于算法岗。它更看重你是否能用C语言把逻辑写清楚,而不是追求最优解。后面我会专门讲编程题的解法。
2.4 嵌入式系统:中断、协议与RTOS
嵌入式系统知识是网易笔试里区分度最大的部分,准备过的同学会觉得送分,没接触过的考生则会很懵。
中断是必考概念。有一道选择题问“在中断服务程序(ISR)中不应该做什么操作”,选项有:置位标志位、执行耗时操作、调用printf、修改全局变量。正确答案是执行耗时操作和调用printf。原因是中断服务程序要求快速响应,如果在里面做耗时操作会阻塞其他中断,影响系统实时性。而printf内部可能涉及系统调用和锁,在中断上下文里是不可重入的。如果选项里明确说明了“调用不可重入函数”,这也是错误选项。
通信协议方面,I2C和SPI的区别是常考内容。I2C是两线制(SDA和SCL),半双工,有设备地址的概念,支持多主多从,速度一般在几百kHz到几MHz;SPI是四线制(MOSI、MISO、SCK、CS),全双工,没有地址概念靠片选选通设备,速度可以跑到几十MHz。UART是异步串行通信,需要双方约定波特率。这些特性用表格整理一下,考前看一眼就够了。
RTOS相关的考了任务调度和优先级反转。优先级反转是经典问题——高优先级任务被低优先级任务阻塞,因为低优先级任务持有了公共资源,而中优先级任务又抢占了低优先级任务的CPU时间。解决办法是优先级继承或者优先级天花板。嵌入式岗位的笔试能考到优先级反转,说明网易确实想招懂系统的人,不是只会点灯的那种。
2.5 计算机网络与Linux基础
计算机网络部分占比不高,但TCP三次握手、四次挥手的题目还是出现了。跟普通开发岗不同,嵌入式岗的网络题更偏向应用层和Socket编程。比如问TCP和UDP区别、select和epoll的区别。多选里有一道关于TCP可靠传输的,哪些机制保证了可靠性?答案是序列号、确认应答、超时重传、滑动窗口。这题如果熟悉TCP协议栈,基本是送分。
Linux基础部分考了grep、awk、sed的用途,还有一个文件权限相关的题:chmod 755 file的含义。755表示所有者有读写执行权限(7),所属组和其他用户有读和执行权限(5)。我这个在平时开发中经常用到,因为是送分题。另外还考了makefile的基础知识,比如目标、依赖、命令的写法,这提醒我们嵌入式岗位确实需要掌握一套完整的Linux工具链。
3. 编程题实战解析
3.1 编程题一:链表去重与排序
网易嵌入式笔试的编程题,我印象最深的一道是链表相关的。题目大意是:给定一个无序单链表,去除重复节点,使每个元素只出现一次,并返回去重后的链表头。要求空间复杂度尽量低。
这道题最简单的解法是双重循环,每个节点都往后扫描一遍,遇到相同的就删除。时间复杂度O(n^2),空间复杂度O(1)。虽然不优雅,但胜在逻辑简单,适合笔试环境下快速写出正确代码。我当时用的是哈希表法:
#include <stdio.h> #include <stdlib.h> #include <stdbool.h> #define HASH_SIZE 1024 typedef struct Node { int val; struct Node *next; } Node; typedef struct HashNode { int val; struct HashNode *next; } HashNode; bool hash_contains(HashNode **hash, int val) { int idx = abs(val) % HASH_SIZE; HashNode *cur = hash[idx]; while (cur) { if (cur->val == val) return true; cur = cur->next; } return false; } void hash_insert(HashNode **hash, int val) { int idx = abs(val) % HASH_SIZE; HashNode *new_node = (HashNode *)malloc(sizeof(HashNode)); new_node->val = val; new_node->next = hash[idx]; hash[idx] = new_node; } Node* remove_duplicates(Node *head) { if (!head) return NULL; HashNode **hash = (HashNode **)calloc(HASH_SIZE, sizeof(HashNode *)); Node *curr = head; Node *prev = NULL; while (curr) { if (hash_contains(hash, curr->val)) { prev->next = curr->next; Node *tmp = curr; curr = curr->next; free(tmp); } else { hash_insert(hash, curr->val); prev = curr; curr = curr->next; } } free(hash); return head; }在笔试现场,不需要把哈希表实现得这么完整,可以简化为一个固定大小的标记数组,如果数据范围已知的话。我在这里展示的是一个通用版本,突出思路。
这道题的关键点在于:删除节点时一定要维护prev指针,否则删除头节点或者连续重复节点时会出问题。还有一点,用完的临时节点要及时free,在嵌入式设备上内存泄漏是不可接受的。做题时举手之劳写上,能给面试官留下不错印象。
3.2 编程题二:字符串压缩
另外一道编程题是字符串压缩:把连续重复的字符压缩成“字符+次数”的形式,比如aaabcccccd压缩成a3b1c5d1。如果压缩后字符串长度不小于原字符串,则返回原字符串。
我第一反应是用双指针找连续相同字符的区间。代码核心逻辑如下:
#include <stdio.h> #include <string.h> #include <stdlib.h> char* compress(char *s) { int len = strlen(s); char *result = (char *)malloc(sizeof(char) * (2 * len + 1)); int idx = 0; for (int i = 0; i < len; ) { int j = i; while (j < len && s[j] == s[i]) j++; int count = j - i; result[idx++] = s[i]; if (count > 1) { idx += sprintf(result + idx, "%d", count); } i = j; } result[idx] = '\0'; if (strlen(result) >= len) { return s; } else { return result; } }这里有一个细节值得注意:题目要求“如果压缩后字符串长度不小于原字符串,则返回原字符串”。如果压缩后的结果长度等于原字符串,比如aabbcc压缩成a2b2c2,长度一样,此时返回原字符串。这个条件很多人会忽略,直接返回压缩结果,导致丢分。我在考试时就因为这个条件多想了半分钟,还好最后注意到了。
字符串题在嵌入式笔试中出现频率很高,因为嵌入式开发经常要处理协议数据、日志输出等文本内容。熟练掌握sprintf、strncpy、memcpy这类常用库函数,能在编程题上节省大量时间。
3.3 手写代码的现场经验
在线笔试的编程题不像面试白板题那样有人看着你写,但同样需要注意代码风格。网易的判卷系统会运行你的代码,但也会有人工复查环节,代码是否规范、注释是否清晰,都会影响最终评价。
我的建议是:哪怕时间紧张,也要保证函数命名有意义,不能出现a1、a2这种变量名。必要的地方加一两行注释,说明自己的思路。代码逻辑比花哨技巧更重要,先把能确定的写法写清楚,再去考虑优化。另外,C语言代码在本地环境跑通之后,记得检查边界条件——空指针、空字符串、单个字符、全相同字符这些情况都要在脑子里过一遍。
还有一点:如果真的做不出来,也不要空着。把思路用注释写出来,或者把主体框架搭好,留下接口,让判卷人看到你有完整的代码结构意识,也能拿一部分分数。
4. 面试与项目准备方向
4.1 简历上的嵌入式项目怎么写
笔试只是校招的第一道关卡,网易正式批的流程是笔试通过后进入面试环节。面试时项目经历是必问的,而且嵌入式岗位的面试官非常看重你是否真的动手做过东西。我在这里分享一下简历上嵌入式项目该怎么写,这个思路适用于任何大厂。
项目描述的结构建议是“背景—方案—难点—结果”四段式。比如你做一个智能家居网关,不要只写“基于Linux的智能网关设计”,要重点突出你在里面做了什么:用了什么通信协议、遇到什么问题、怎么解决的、最终效果如何。例如:“使用MQTT协议实现设备端与云平台通信,自行设计并实现了断线重连与消息缓存机制,解决了弱网环境下消息丢失的问题”。这样的描述既有技术含量,又能体现问题解决能力。
项目不用追求高大上,但一定要是你自己真正做过的,能讲清楚每一个细节。面试官最反感的就是简历包装过度,一问细节就露馅。一个完整的STM32项目、一个Linux驱动模块、一个基于ESP8266的物联网项目,哪怕难度不高,只要你能讲透,效果都比写一堆“精通”却没有细节强得多。
4.2 嵌入式项目的常见类型与建议
我给准备校招的同学推荐三类嵌入式项目,每一类都能覆盖笔试和面试中的核心考点:
第一类是STM32+传感器的小型系统。比如做一个环境监测系统,用STM32读取温湿度传感器数据,通过OLED显示,用按键控制阈值,超限时蜂鸣器报警。这个项目能覆盖GPIO、中断、定时器、I2C/SPI协议、状态机设计等知识点。面试时可以主动聊一聊你在配置中断优先级时踩过的坑,以及如何通过状态机避免按键抖动造成的误触发。
第二类是Linux下的驱动或应用开发。比如在树莓派或开发板上写一个字符设备驱动,实现简单的读写和IO控制功能。这个项目需要你理解file_operations结构体、设备号分配、copy_from_user/copy_to_user的使用,以及内核编译和模块加载的流程。这类项目在网易这类大厂的面试中含金量很高,因为他们的嵌入式岗位大量涉及Linux环境。
第三类是带物联网协议的应用项目。比如基于ESP32采集传感器数据并上传到MQTT服务器,手机端通过订阅主题实时查看数据。这类项目可以引出TCP/IP协议、MQTT协议格式、JSON数据解析、低功耗设计等话题,也很贴合网易在智能硬件方向的业务需求。
无论哪种项目,都要准备好回答三个问题:这个项目的整体架构是什么?你负责的具体模块有哪些?如果重新做一遍,你会改进什么?第三个问题尤其能体现你的思考深度。
4.3 面试高频问题与作答思路
面试环节的高频问题和笔试考点高度重合,但问得更深。比如笔试考了“中断服务程序里不能做什么”,面试就可能会追问“为什么不能用printf”,需要你答出printf可能引发重入、阻塞和调度问题。再比如笔试考了“进程和线程的区别”,面试就可能追问“为什么调度线程的开销比进程小”,需要你说清楚上下文切换的具体过程。
网易的面试官很喜欢基于你的项目深挖底层原理。如果你在简历上写了“用I2C协议驱动传感器”,面试官可能就会问:I2C的起始条件是什么?应答信号是什么?如果设备不应答该怎么办?所以做项目的时候不要只停留在调库的层面,要把协议时序、寄存器配置、调试方法都搞清楚。
另外一个高频问题是“对嵌入式软件工程师这个岗位的理解”和“未来3-5年的职业规划”。这类开放型问题没有标准答案,但建议结合岗位需求去答。嵌入式软件工程师的成长路径一般是“底层驱动开发—系统移植与定制—系统架构设计”,你可以表达自己对底层技术的兴趣,以及希望在某个技术方向上深耕的意愿。
5. 备考路线与避坑经验
5.1 时间规划与复习节奏
校招备考是一个长期过程,建议按3到4个月的时间跨度来规划。前两个月做基础复习,覆盖C语言、数据结构、操作系统、计算机网络的考点,这个阶段可以配合经典书籍和网上刷题;第三个半月集中刷笔试题,尤其是大厂历年真题,通过刷题查漏补缺;最后一个半月到考前,就专注于自己的项目整理和模拟面试。
我踩过的一个坑是前期漫无目的地看操作系统教材,看了两周还在看进程管理,覆盖面不够。后来我改成“考点驱动”的学习方式:先做一套真题,把错题涉及的知识点都列出来,按知识点进行针对性学习。这样既高效又不会遗漏重点。等到后面复习差不多了,再去做整套的真题,检验自己的综合水平。
5.2 推荐的复习资料
嵌入式方向没有一份“完全对口”的笔试复习资料,因为大厂的笔试题往往横跨多个领域。我推荐用组合拳的方式复习:
- C语言:《C Primer Plus》适合基础查漏补缺,或者网上搜“C语言经典笔试题”,性价比更高
- 数据结构:不要去看厚厚的算法导论,笔试难度用《大话数据结构》或者直接刷题就够了
- 操作系统:重点看进程线程、同步互斥、死锁、内存管理,配合《深入理解计算机系统》的虚拟内存章节
- 嵌入式系统:重点看中断、通信协议、RTOS概念,这部分我推荐看一些开发板的教程,比如正点原子或野火,里面讲得比较通俗
- Linux:不需要系统啃书,重点掌握常用命令、编译链接过程、makefile的写法
这些资料不需要全部看完,根据笔试考点做减法更重要。
5.3 在线笔试的临场经验
在线笔试和线下笔试差异很大,最大的问题是容易受环境干扰。我建议考试前做这几件事:找一个网络稳定的地方,把手机静音放到拿不到的位置,提前半小时调好摄像头角度,关闭所有无关的浏览器标签页和应用。还有一点很重要——提前准备好草稿纸和笔,编程题在动笔之前先画一画思路,能明显减少代码错误。
答题顺序上我是先做自己有把握的,再回头啃难题。编程题如果一时没有思路,先做后面的选择填空,让大脑后台处理一下,往往过一会儿回来看就有思路了。考试时间管理上,建议给选择题留40分钟,编程题留50分钟,剩下半小时检查和思考难题。网易的题量不算大,主要还是看正确率。
5.4 我踩过的坑与复盘
最后分享几个我实际踩过的坑,希望后来者能少走弯路。
第一个坑是轻视了多选。有些同学觉得多选题分值不大,就随意选,结果错选扣分严重,拉低了整体分数。多选一定要用“少选保分”的策略,不确定的选项不要选。
第二个坑是编程题里用了C++特性却忘了切换语言。网易的笔试环境支持多种语言,但如果你选了C语言,代码里就不能用std::vector或者auto。我有一道题用C++语法在C语言的编译器下编译,一直报错,浪费了很多时间。做题前先确认好语言设置,或者干脆所有代码都只用C语言基础语法写。
第三个坑是复习时没有关注网易的业务方向。网易做游戏、音乐、电商、教育,还做了不少智能硬件和物联网相关的东西,笔试里会出现一些偏业务的场景题,比如“在一个智能音箱的音频采集模块中,你会如何设计环形缓冲区”。这类题考察的是解决实际问题的能力,平时做项目时多思考一下数据结构在实际场景中的应用,就能答得不错。
第四个坑是没有准备项目中被追问的深度问题。我当时只准备了项目在做什么,没仔细想底层实现的细节,结果面试官追问“你用的那个传感器的I2C地址是怎么确定的”时,我明显卡住了。后来我养成了一个习惯:每做完一个项目,都要从寄存器、协议时序、调试方法三个层面重新审视一遍,确保每个模块都能被追问到底。
写在最后
从笔试到面试,网易嵌入式软件工程师的整个校招流程,其实都在验证一件事:你是不是真的理解嵌入式系统从硬件到软件每一层的运行逻辑。笔试只是入门,通过它检验你是否具备C语言、操作系统、数据结构、嵌入式基础这些硬实力;面试才是真正拉开差距的地方,项目深度和原理理解决定你能走多远。
我个人觉得,准备这类校招最有效的状态不是堆砌多少道题、背多少条知识点,而是把每一个考点都变成“我做过、我踩过坑、我知道为什么”的经验。嵌入式这个方向门槛看起来不高,但越往深处走,越需要扎实的基本功。希望这篇复盘能帮你在准备笔试的路上省下一些摸索的时间。
