前两天整理移动硬盘,翻到了当年参加小鹏汽车2019春招车联网软件工程师笔试时留下的复盘文档。那会儿正值春招补录,岗位挂靠在互联网中心,名字叫“车联网软件工程师”,但看了笔试内容就会发现,它既没有去考CAN总线解析,也没有考高精地图和SLAM,更多还是集中在软件工程师的基本盘上。今天把这份笔试题的考察逻辑重新捋一遍,也是给正在准备车企软件岗面试的朋友一个参考:车联网方向笔试题并不是什么“玄学”,所有题目都指向同一个问题——你能不能在一个资源不算宽裕的车载环境里,写出稳定、可维护、能上线的软件。
这份笔试如果落在今天来看,题型其实不算特别新颖,C/C++、数据结构、操作系统、网络协议、算法编程一圈下来,看起来和互联网后端校招差别不大。但仔细做进去会发现,它选的知识点非常克制,偏向嵌入式场景和通信场景的交叉地带。这也提醒我们:准备车联网软件工程师,不只是刷LeetCode,还要把Linux、网络协议、系统资源这些“传统地盘”捡起来。文章后面我会按当年笔试涉及的知识块逐一展开,也会把我重做了一遍后想明白的细节写出来。
1. 互联网中心招的“车联网软件工程师”和嵌入式开发有什么不同
1.1 同一个“车联网”标签,不同岗位的权重差别很大
很多人在投“车联网软件工程师”时,会下意识把它等同于嵌入式软件开发。实际上,车企的软件岗位分得很细,车联网方向也存在明显的业务边界。以当年小鹏汽车互联网中心挂出的岗位来说,它要解决的更多是“车端如何和云端互通”“用户如何通过手机与车辆交互”“车辆运行数据如何稳定上报”这些问题。换句话说,这个岗位处在传统汽车电子和移动互联网的交叉点上。
这种定位直接影响了笔试选题。纯嵌入式岗位大概率会考寄存器配置、中断处理、I2C/SPI时序,甚至让你分析某块SoC的启动流程;而互联网中心的车联网软件工程师岗位,更关心你能不能写对网络通信模块、能不能处理并发上报、能不能在Linux环境下定位一个崩溃问题。所以笔试里出现大量C/C++和Linux基础,但很少出现芯片级驱动题目,也就不奇怪了。
1.2 从岗位能力画像反推笔试的底层逻辑
如果仔细拆解岗位要承担的工作,会得到下面这张能力画像:
| 能力维度 | 为什么要考 | 在笔试里的对应题型 |
|---|---|---|
| C/C++语言 | 车机端应用、T-Box通信模块大量使用C/C++ | 指针、内存、类相关选择题和改错题 |
| 数据结构与算法 | 软件工程的基本功,决定代码质量 | 链表、二叉树、栈队列的手写与复杂度分析 |
| 操作系统 | 车机系统需要应对多任务调度、资源紧张 | 进程线程、死锁、信号量、内存管理 |
| 计算机网络 | 车联网的本质是车与云端、手机之间的通信 | TCP/UDP、HTTP、MQTT概念题 |
| Linux操作 | 车机系统普遍基于Linux/Android | 常用命令、简单脚本编写 |
这个画像并不要求每一块都达到专家水平,但要求每一块都不能有明显短板。笔试的筛选逻辑也很有意思:它不通过偏题怪题来制造门槛,而是通过“基础题密度”来淘汰训练不足的人。很多人觉得车联网工程师岗位很酷,考前拼命去补自动驾驶、激光雷达、高精地图,结果一上笔试发现连struct内存对齐都能做错,这就很可惜。
1.3 “互联网中心”这五个字透露出的关键信号
岗位名称里带“互联网中心”,意味着这个团队的工作模式更接近互联网团队:有较快的迭代节奏,讲究代码评审和工程质量,也会关注数据上报、服务端接口、App联动这类偏上层的事情。笔试里出现“车辆状态上报”“远程控制指令怎么设计”这类题目,其实就是在考察你有没有互联网软件的思维习惯。
我当时还注意到一个细节:题目里对内存和性能的关注明显比纯互联网后端岗位要高。原因是车机的硬件资源远不如服务器那么充裕,一行不规范的字符串处理代码,就可能在某个低配车机上引发卡顿甚至崩溃。所以笔试考内存对齐、考动态内存管理,不是故意刁难,而是这个岗位每天都会遇到这些事。
2. 从岗位描述反推笔试范围:基础能力与车联网场景的权重
2.1 岗位描述里那些关键字的优先级
当年招聘信息里的关键字,我现在还记得大概:车联网、软件工程师、嵌入式、Linux、通信。把这些词展开,就是笔试考察范围。校招笔试不会考你上一份实习做了什么,它更倾向考察“你这个人有没有独立成长的基础”。所以从岗位描述能直接推出好几个可能考到的模块:
- 如果写了Linux,大概率会考常用命令、进程概念、文件系统;
- 如果写了通信,大概率会考TCP/IP协议栈,运气好会碰到MQTT这类物联网协议;
- 如果写了嵌入式,C语言的优先级会直接拉到最高,指针、内存、结构体这些跑不掉;
- 如果写了软件工程师,数据结构与算法基本是必考题。
注意这里的权重分配:基础能力是主体,车联网场景是包装。就像互联网后端笔试会把题目包装成“订单系统”一样,车联网笔试也喜欢把题目包装成“车辆状态上报”“远程升级任务”。你看穿这点之后,复习就不会跑偏。
2.2 必考、常考、加分考点的优先级表
结合当年实际笔试的体感,如果把考点排个优先级,大概是这样的:
| 优先级 | 考点 | 常见出题方式 |
|---|---|---|
| 必考 | C语言基础、指针、内存 | 选择题、代码找错 |
| 必考 | 数据结构:链表、栈、队列、树 | 手写代码或复杂度分析 |
| 必考 | 操作系统:进程线程、同步、死锁 | 概念选择题、问答 |
| 常考 | Linux命令、shell脚本 | 命令填空、场景脚本 |
| 常考 | 网络协议:TCP/UDP、HTTP | 问答、流程描述 |
| 常考 | 算法题:字符串、数组、简单动态规划 | 在线编程题 |
| 加分 | MQTT、JSON/Protobuf、OTA流程 | 场景问答 |
你可以发现,车联网特有的知识点通常不会单独出一道大题,而是作为背景嵌入到网络或操作系统题目里。例如“车辆在弱网环境下上报位置,应该选TCP还是UDP”,表面考网络,实际考的是你对可靠性和实时性的权衡。这种题目如果只背概念而不理解场景,很容易答偏。
2.3 为什么2019年春招的笔试会更偏重基础
那一年春招的节奏比秋招快,留给笔试的时间窗口短,面试官也需要通过一场笔试快速筛选出值得进入后续流程的人。所以笔试题目不会出得太难,更不会为了一个冷门知识点让大多数人交白卷。它的目的是把“基础扎实、具备计算机系统观”的人挑出来,而不是找“百科全书式”的人。
这也意味着,备考重点应放在高频考点上。如果你花大量时间去背车联网行业分析、研究每一个OEM的远程升级方案,反而可能错过最核心的得分点。把基础题练到肌肉记忆,再去拓展车联网场景题,才是稳妥的路。
3. 基本盘拆解:C/C++、数据结构、操作系统在笔试中的考法
3.1 C/C++的送分题与争夺题
C/C++是当年笔试里分量最重的一块。它考察的难点不在语法本身,而在你是否真正理解内存布局和对象生命周期。随便举几个高频点:指针和引用的区别、const在C和C++中的不同含义、static变量和全局变量的区别、结构体内存对齐、虚函数底层实现。这些知识点非常基础,但能全答对的人并没有想象中那么多。
以结构体内存对齐为例,题目往往问一个结构体占多少字节:
struct Node { char a; int b; char c; };如果在32位系统上默认4字节对齐,答案是12字节,而不是6字节。很多人只算字段宽度,忘了对齐填充。这个考点几乎年年出现,因为它在嵌入式通信中非常关键,结构体经常用来解析CAN报文或者网络帧,如果对齐规则不清楚,很容易踩到协议解析的坑。
另一个值得写的是浅拷贝和深拷贝。车联网代码里经常会出现车辆信息结构体的赋值,如果只做浅拷贝,动态分配的字符串就会被两个对象同时持有,析构时就会double free。笔试里可能不会让你写完整代码,但会给出一个类,让你指出拷贝构造和赋值运算符有什么问题。这类题逻辑不复杂,前提是你平时真写过、真崩过。
C++里还有个容易考的细节:为什么构造函数不能是虚函数,而析构函数推荐写成虚函数。在车辆通信模块中,上层往往定义抽象接口,下层有多种协议实现,比如4G、Wi-Fi、蓝牙。如果基类析构函数不是虚函数,delete基类指针时子类资源就无法释放。这个点既考语言机制,也考工程意识,答到“资源释放”层面通常就能拿高分。
3.2 数据结构题:不只考手写代码
数据结构的选择题通常是送分题,比如栈和队列的异同、哈希冲突的几种解决方式、二叉搜索树的查找复杂度、快排最坏时间复杂度。这部分只要刷过一遍基础题,基本不会丢分。容易拉开差距的,还是手写代码题。
链表反转出镜率很高。它看起来简单,但要在五分钟内写对,手不能生。参考答案思路如下:
struct ListNode* reverseList(struct ListNode* head) { struct ListNode *prev = NULL, *curr = head; while (curr) { struct ListNode *next = curr->next; curr->next = prev; prev = curr; curr = next; } return prev; }除了链表,二叉树层序遍历、用两个栈实现队列也称得上高频。这些题不考技巧,纯粹考你平时有没有动手写过。如果没有,在笔试现场边想边写,非常容易卡壳。另一个值得注意的考点是复杂度分析。有些题目不要求你写完整代码,但会问你“将哈希表改为红黑树后,查找复杂度从O(1)变成多少”,这需要你对数据结构的底层实现有基本感知。
3.3 操作系统与Linux:嵌入式环境下的实操感
操作系统题很少考特别抽象的理论,更多是问进程和线程的区别、死锁的四个必要条件、信号量和互斥锁的使用场景。如果你有过多线程编程经验,答起来会轻松很多。车机场景里,多个应用要同时访问定位模块、网络模块,这里就是典型的同步与资源管理问题。
当年的题目里,我最深的一道是“多个线程同时往一个日志缓冲区写数据,如何保证不互相覆盖”。这个问题表面考线程安全,实际考你对锁的理解。正确的回答是先想到互斥锁或自旋锁,然后再补充一句“锁的粒度尽量小,避免日志写入过程中长时间持有锁导致其他线程卡顿”。如果还能想到用无锁环形缓冲区,那就是加分中的加分了。
Linux方面,命令题是性价比最高的部分。下面这几条建议你考前闭眼都能写出来:
- 用
top或free查看系统负载和内存占用; - 用
ps -ef | grep 进程名查找进程PID; - 用
netstat -tunlp查看端口和网络连接; - 用
find /var/log -name "*.log" | xargs grep xxx在日志目录里查关键词; - 用
tail -f实时跟踪日志; - 用
kill -9 PID强制结束进程。
笔试里可能会让你写一个简单脚本,比如“删除一周前的日志文件”。很简单:
#!/bin/bash find /var/log/vehicle -name "*.log" -mtime +7 -exec rm -f {} \;这其实也考察了find条件的组合能力。如果你只会ls和cd,到这就露馅了。Linux不是一天练成的,但常用命令突击一两天就能覆盖大部分考点,投入产出比很高。另一个常见问法是“如何查看某个进程的CPU和内存占用”,很多人会答ps,但top -p PID才是更有针对性的答案,面试官会更认可这种精确操作。
4. 提分项拆解:网络通信与算法编程题怎么准备
4.1 TCP/UDP和车联网场景的结合
网络题是车联网软件工程师笔试里最具岗位特色的部分,因为它能真正区分“背过八股文”和“理解场景”。基础题大家都准备过:三次握手为什么不是两次、四次挥手为什么需要TIME_WAIT、UDP和TCP各自适合什么场景。但真正有区分度的,是把它放到车联网语境里问。
举个例子,题目可能会说:车辆在行驶过程中需要每10秒上报一次GPS位置,请问选用TCP、UDP还是MQTT更合适?很多人一看到“上报”就写UDP,理由是实时性好。但如果你了解实际工程,会发现位置上报也需要考虑数据完整性,因为服务器端要用这些位置绘制轨迹、分析驾驶行为。完全丢包会导致轨迹断裂。所以工程上常用MQTT QoS1,既保证至少一次到达,又比裸TCP连接重建的开销小。
再比如远程控车指令,这种操作要求可靠性和安全性。车门解锁指令一旦丢失,用户可能被锁在车外。所以它必须走加密的可靠通道,HTTP/HTTPS或MQTT QoS2都可行。笔试答题时,如果能写出“可靠指令用TCP/HTTPS,高频低价值数据用UDP或MQTT QoS0”这种层次感,阅卷人一眼就能看出你不只是背了协议区别。
还包括HTTP和HTTPS的区别,这个常规考点在车联网里也有延伸:车机请求云端接口,证书校验失败应该怎么办?如果直接忽略证书继续请求,就可能被中间人攻击。好的回答应该是“先判断失败原因,再决定是否走安全策略;涉及车辆控制类接口,必须严格校验”。这种安全意识在车联网岗位里非常看重。
4.2 算法题的难度和取舍
算法编程题通常是整个笔试里最耗时间的。当年的题目难度基本在LeetCode中等偏下,不太会出现竞赛题。常见方向包括:字符串处理、数组操作、链表、二分查找、简单动态规划。例如“合并两个有序链表”“最长无重复子串”“两数之和”这类。
写题时有个很重要的取舍:拿到题目先不要急着写代码,先跟出题人给的例子过一遍流程,确认输入范围和边界。比如合并两个有序链表,它考察的不只是逻辑,还包括“有没有处理空链表”“有没有理清dummy节点”这些小节。代码可以写得朴素,但一定要完整:
struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2) { struct ListNode dummy; struct ListNode* tail = &dummy; dummy.next = NULL; while (l1 && l2) { if (l1->val < l2->val) { tail->next = l1; l1 = l1->next; } else { tail->next = l2; l2 = l2->next; } tail = tail->next; } tail->next = l1 ? l1 : l2; return dummy.next; }如果运气好碰到“最长无重复子串”,可以用滑动窗口解决。关键是想清楚窗口什么时候收缩,什么时候更新答案。这类题只要保持刷题手感,临时上手不会太难。还有一个方向也值得留意:排序类题目。比如给一组车辆状态记录按时间戳排序,你会优先写快速排序还是直接调用系统库?工程上当然是调用系统库更稳妥,但笔试里如果明确要求“手写排序”,就必须能在纸上写对partition过程。
4.3 答题规范与调试技巧
在线笔试平台通常没有代码提示,也不提供完整编译调试环境。很多人写完代码后没有测试用例的意识,丢分常常不是思路不对,而是“没考虑到空输入”。我的建议是:写完之后,至少在脑子里跑三组用例——正常输入、空输入、单元素输入。如果平台支持本地编译,就先把代码在本地跑一遍再贴上去,稳得多。
另外,注意输入输出的格式。牛客和赛码这类平台,经常要求你处理多行输入,如果scanf或cin用错,代码逻辑再正确也可能超时或者读不到数据。这些都是细节,但细节决定笔试能不能进面。还有一个很实用的习惯:在代码开头写清楚思路注释,哪怕只有两三行。如果线上判题没给满分,后续人工复核时,看到你在注释里写了“用快慢指针找中点,再用归并排序”,也能给出印象分。
5. 90分钟作答的节奏控制与真实踩坑复盘
5.1 我当年的时间分配方案
当年笔试时长是90分钟,题目类型包括选择题、填空题、简答题、编程题。很多人到最后没有做完,不是题目量太大,而是没有控制节奏。我自己的时间分配习惯是这样的:先快速浏览整张试卷,判断题量比例;接着用10到15分钟解决所有选择题和填空题;给编程题留出40分钟;再用剩余20分钟处理简答题和检查。
选择题和填空题是最容易拿分但最容易被拖时间的部分。有些C语言题很刁钻,比如“有符号和无符号比较”“整型提升”,如果你不确定,不要恋战,先做一个标记,回头再来看。编程题必须留足时间,因为一旦写不完,几乎拿不到分数。简答题不要写太长,按照“定义、原因、场景、做法”四段式组织,每条控制在三四行,既清楚又不浪费时间。
5.2 那些看着会做却丢分的瞬间
复盘当时答题卡,我发现最可惜的丢分点不是不会做,而是“会做但没写全”。比如某道简答题问“为什么车机远程升级要设计多个版本回退机制”,我光写了OTA升级失败会导致设备变砖,但没有展开说明版本校验、A/B分区、升级包签名这些工程细节。阅卷人想看的不是一句口号,而是你真的了解一个软件系统在真实设备上会遇到的边界情况。
编程题也踩过类似坑。我记得有一道题要求“输出合并后的链表节点值”,我写完合并逻辑后忘记逐节点输出,直接输出了头节点指针,平台判题必然报错。这种错误不是能力问题,而是考场状态下习惯性忽视“输出格式”这四个字。还有一次是写代码之前没初始化指针,本地编译器开启了严格警告所以能发现,但线上平台可能直接编译失败。
5.3 重新复盘后的心得
整理完这份复盘,我最大的感受是:车联网软件工程师笔试的核心不是“卷难度”,而是“考稳定”。它希望筛出来的,是能在有限时间内把基础题做对、把关键场景想清楚、把代码写得不出边界问题的人。
如果让我重新准备一次,我会把复习顺序调整为:先快速刷一遍C语言和数据结构基础题,确保不犯低级错误;再花两三天把Linux命令和shell脚本练熟;接着把TCP/UDP、MQTT、HTTP这些协议在车联网场景里的应用想透;最后才是刷算法题。这个顺序不是按分数占比排的,而是按“投入产出比”排的——基础题和命令题背了就有分,算法题则依赖长期积累,冲刺阶段性价比相对低。
我还想特别提一点:面试官很看重“排查问题”的思维方式。笔试里如果出现“车辆偶发不上报数据,如何排查”这样的场景题,不要一上来就写“我是后端工程师,不擅长”。把链路拆成车端采集、网络传输、云端接收三段,每段列出可能的故障点和排查命令,比如车端看进程是否存活、网络层看TCP连接状态、云端看日志和数据库写入。这种结构化表达,即使不是标准答案,也能展现出你的系统思维。
最后再分享一个简单但有用的技巧:笔试前把常见的结构体对齐、三次握手、进程切换、链表反转、滑动窗口这五类题各默写一遍。字不用多,关键是手要熟。很多时候你感觉自己“会”,但真正动笔才发现缺了一个dummy节点。这份2019春招的笔试题已经过去好几年了,但这类岗位要的东西其实一直没变:基础扎实、懂场景、能落地。如果你正在准备类似的车联网软件工程师岗位,不妨把这份复盘当作一份最小检查清单,逐项确认一下自己是否真的准备好了。