守得云开见月明
2020年是不平凡的一年,时至今日疫情的阴云仍未散去,中美贸易战硝烟仍在,芯片制裁犹附骨之疽让国内企业咬牙切齿却又无可奈何。这样的特殊背景下必然决定了今年校招的难度提升,回顾我一路走来的校招历程,从阴云密布到云开月明,虽没有赶上晨曦,但也有晚霞相伴,希望记录一下人生这匆匆一瞥,与君共勉。
我的专业背景是电子,本科和研究生期间做的都是很硬件的项目,接触过电子设计、MCU、FPGA这些基础的东西。软件方面只学过C,了解一些C++,所以代码能力和CS的基础几乎为0。嵌入式软件这个岗位考察的很多都是计算机相关的知识和技能,所以一切都要从头学起。由于特殊原因,研一研二没有时间去学习这些,所以前面欠下来的债只能后面一点一点去弥补了。
今年4月中旬,在小论文投出去之后我的校招之路正式开始。分为这么几个阶段。
- 4月中旬-5月初
复习C语言,学习数据结构和算法,尝试在leetcode上开始刷题。期间做了华为的实习生笔试,只做出来了第一道链表相关的简单题,阿里的笔试对着屏幕发呆了一个半小时,啥也没写出来。 - 5月初-6月中旬
这阶段重点放在了ARM嵌入式linux上,主要是内核部分。对着一块开发板,跟着文档以及视频开始学习。从uboot、kernel、rootfs的移植,到设备驱动的编写。期间看了一点内核的源码,我只能说里面的水很深XD。由于自己单片机玩得还可以,设备驱动的核心思路是有共通点的,而且对这部分比较感兴趣,学习过程也非常开心。小论文的返修意见也回来了,幸运的是评审专家对我的idea总体还是很认可的。 - 6月中旬-7月底
之后我开始学操作系统原理和计算机网络这两门基础课,由于是比较理论的部分,很难对这些提起兴趣,所以自学效率也比较低。期间通过了学校的英语考试,终于修满了学分。参加了六级考试,后面查到成绩仍然是低分飘过。vivo打响了校招提前批的第一枪,在考试当天我开开心心地点开笔试链接,结果发现自己被移除了考试名单。那时的我无论如何都不会想到自己的校招之路从一开始到最后竟是如此艰辛。tplink、瑞芯微、汇顶、美的提前批直接被表刷。中智行、乐鑫提前批的笔试由于自己太菜没过,甚至让我对coding产生了恐惧。为了更好地训练算法,我放弃了C语言刷题,补了一些C++的STL库以及面向对象的知识,开始用C++。 - 8月
校招正式批来了!今年的时间特别早。dji的笔试编程题对我直接造成了暴击,因为大疆一直是我最想去的公司,没想到自己还是倒在了算法题上。后面陆陆续续笔试面试了一些公司,仍然没有什么起色,代码能力和业务能力太差了,要项目没项目要实习没实习。痛定思痛,后面自己调整了做题策略,勤做总结,代码能力算是稍微有些起色了,自己又继续深入学习了一些linux系统编程相关的知识,包括文件I/O、进程、线程等。万万没想到,校招是我研究生生涯成长最快、学习知识最多的阶段。 - 9月-10月
身边同学的捷报频传,offer薪资让我目瞪口呆,因为对于我来说都属于天文数字。反观自己仍是颗粒无收。同样的学校,同样的实验室,却形成鲜明的对比。我不禁感叹,选择真的大于努力。没办法,我告诉自己,stop complaining and keep going on。
【之前做过的题今天就完全没思路了?】没关系,就当新题重新做就是了。【今天早上看的知识点,晚上就忘记了?】小事情,大不了再读一遍呗。【面试过程中因为专业背景被面试官鄙视了?】这算啥,我选择不了项目方向,但我可以通过自学。【“什么,这么简单的手撕代码题你写了这么长时间?”】不是事,我至少写出来了,后面肯定会写得更好。【“对不起,你没达到我们公司的要求。”】就这?那我再换一家,拜拜了您嘞。
在无数次的碰壁、怀疑自己、释怀、继续加油这样的循环下,自己的心态逐渐强大,信心也有了很大的提升,在9月底-10月初终于陆陆续续收到了一些offer。最后自己选择了一家芯片原厂,工作内容是kernel、driver、bsp。终于结束了漫长折磨的秋招。
而今迈步从头越
回顾校招之路,自己总共投递了60+企业,在这里记录一些印象比较深的笔试面试,如果能对别人起到一些微小的帮助,那么这篇文章也就得到升华了。
PS推荐一些书籍:《C和指针》、《后台开发 核心技术与应用实践》、《剑指offer》、《大话数据结构》、《Linux内核设计与实现》、《Linux程序设计》。
地平线机器人提前批 FAILED
岗位是嵌入式应用工程师。由于是内推,直接进入面试环节。。- 一面
简历的每个项目都深入地进行了解;问了几个C/C++的问题,比如C++虚析构的作用、程序编译过程;手撕代码题是top k问题,即数组第k大的元素(快排、优先队列、heap解法都可),以及一道链表题,找相交链表的交点(先求得两链表长度,然后长短链表同步后按相同步频找相同节点)。 - 二面
static变量的作用;项目中怎么选择多线程架构或多进程架构;codedump用过吗;手撕代码题是将字符串转换成数字,十六进制或者十进制(需要判断字符串是否合法,细节比较多) - 三面
面试官(应该是主管)拿着我的简历问了半天,觉得我的项目比较偏硬件,最后说把我推到bsp(当然是没后续了)这一轮面试我直接光速下播。
- 一面
华为提前批 FAILED
岗位是Carbu的嵌入式软件工程师。面试的问题我回答的不是很好,手撕代码题也没做出来(想掐死自己,这明明做过的题)。- 一面
C++重载和重入;vector原理;引用和指针的区别;C的静态变量和全局变量储存区域,栈是向上增长还是向下增长;linux中断上半部和下半部,上半部能被打断吗;内核同步方法和区别;插入排序原理、是否稳定;手撕代码题是从先序和中序还原二叉树。
- 一面
小马智行pony.ai FAILED
岗位是硬件工程师—嵌入式。顶级独角兽小马的笔试面试体验都挺好的,是我感觉最“嵌入式”的体验,无奈自己硬实力不够。笔试
基础+编程
1获取头文件的宏定义字符串。宏定义字符串有三种形式:cheerUp12.h;CheerUp12.h;cheer_up12.h,,都要转换为CHEER_UP12_H_#include <stdbool.h> #include <stdio.h> #include <string.h> #include <stdlib.h> char changeChar(char c) { if (c >= 'A' && c <= 'Z' || c == '_') return c; else if (c >= 'a' && c <= 'z') { int temp = c - 'a'; char ans = 'A' + temp; return ans; } else if (c == '.') return '_'; return c; } char *GetFilenameDefine(char *filename, char *target_str, int target_str_size) { //获取头文件的宏定义字符串 //char *target_str = (char *)malloc(100); int len = strlen(filename); int i = 0; int j = 0; for (j = 0; j < len - 1; ++i,++j) { target_str[i] = changeChar(filename[j]); if (filename[j+1] >= 'A' && filename[j+1] <= 'Z') { target_str[++i] = '_'; } } target_str[i] = changeChar(filename[j]); target_str[++i] = '_'; target_str[++i] = '\0'; return target_str; } int main() { char target_string[1000] = {0}; char filename[1000] = {0}; scanf("%s", filename); printf("%s\n", GetFilenameDefine(filename, target_string, sizeof(target_string))); }
2通配符匹配类似于leetcode44,就不贴代码了
一面
对每个项目抠的很细,比如说我有个项目是QT写的上位机,通过串口接收单机的数据包并解析。面试官围绕着UART问了很多细节的问题,以及提出了一个新业务场景,让我讲一讲解决的思路;基础知识方面问了动态库静态库,内核态和用户态的通信,系统调用,uboot和内核移植等;手撕代码题是通过a+2,a*3+2这两种计算方式的任意组合,验证能否由a变成b,我当时写了个回溯的方法。二面
项目同样问的比较深入,C++问了很多,比如说多态的实现原理,成员函数和普通函数的区别,芯片架构的问题,常用的通信接口等。手撕代码是对链表进行插入排序。三面
问了一些嵌入式系统的问题,侧重于系统架构、系统设计。算法方面口述一个业务场景的代码思路。这一轮面试我发挥得很不好。
汇顶正式批 offer
笔试
基础知识+一道编程
用c语言实现:给定一个字符串str和另一个字符串sub,统计str中sub出现的次数。不能使用库函数。
当时笔试写的代码也比较挫。#include <stdio.h> int strlength(char *str) { int num = 0; while(str[num] != '\0') { num++; } return num; } int del_sub(char *str, char *sub) { int strlen = strlength(str); int sublen = strlength(sub); //printf("strlen:%d\n", strlen); //printf("sublen:%d\n", sublen); if (strlen < sublen) return 0; int ans = 0; int i, j; for (i = 0; i <= strlen - sublen; ++i) { if (str[i] == sub[0]) { for (j = 1; j < sublen; ++j) { if (str[i + j] != sub[j]) break; } if (j == sublen) ans++; } } return ans; } int main() { char str[100]; char sub[100]; int num = 0; scanf("%s", str); scanf("%s", sub); num = del_sub(str, sub); printf("%d", num); return 0; }
面试
汇顶三面是连在一起进行的,分别为技术面、hr面和主管面。整个过程一口气走下来也比较刺激。
美团 FAILED
岗位是嵌入式系统工程师。感觉这个岗位没有hc,面试官的部门都是软件那边的。- 一面
new和malloc区别;struct字节对齐;hash原理;vector原理;内存管理;线程锁的用法和区别;手撕代码题是简单的二分查找,链表的基础操作。 - 二面
全是简历项目,我觉得面试官对我不是很感兴趣,技术问题没怎么问。
- 一面
中兴 FAILED
我提前批被表刷了,正式批做完笔试后10月份才通知我面试,因为已经做好选择了,没有去面试。笔试
基础题+两道编程
1全班n个学生,m门成绩,老师要给至少一门成绩超过平均分的同学发短信,要发几个同学#include <iostream> #include <string> #include <vector> #include <algorithm> #include <unordered_map> using namespace std; class Solution { private: unordered_map<int, bool> hash; int total; public: void ifAboveAverage(vector<int> nums) { int sum = 0; int len = nums.size(); double ans; for (int i = 0; i < len; ++i) { sum += nums[i]; } ans = (double)sum / (double)len; for (int i = 0; i < len; ++i) { if (nums[i] > ans && !hash[i]) { total++; hash[i] = true; } } } int calMessageCount(vector<vector<int>>& res, int n, int m) { int peopleNum = n; //col len int subjNum = m; //row len total = 0; for (int i = 0; i < subjNum; ++i) { ifAboveAverage(res[i]); } return total; } }; int main() { int n, m; cin >> n >> m; vector<vector<int>> res(m, vector<int>(n, 0)); for (int i = 0; i < m; ++i) { vector<int> temp(n, 0); for (int j = 0; j < n; ++j) { cin >> temp[j]; } res[i] = temp; } Solution solve; cout << solve.calMessageCount(res, n, m) << endl; return 0; }
2 至少去除数组中多少个数能使数组成为单调序列。类似leetcode300
#include <iostream> #include <string> #include <vector> #include <algorithm> #include <unordered_map> using namespace std; class Solution { public: int calMinUp(int len, vector<int>& nums) { vector<int> dp(len, 0); int maxV = 1; for (int i = 0; i < len; ++i) { dp[i] = 1; for (int j = 0; j < i; ++j) { if (nums[i] > nums[j]) dp[i] = max(dp[i], dp[j] + 1); } maxV = max(maxV, dp[i]); } return len - maxV; } int calMinDown(int len, vector<int>& nums) { vector<int> dp(len, 0); int maxV = 1; for (int i = 0; i < len; ++i) { dp[i] = 1; for (int j = 0; j < i; ++j) { if (nums[i] < nums[j]) dp[i] = max(dp[i], dp[j] + 1); } maxV = max(maxV, dp[i]); } return len - maxV; } int calMinTimes(int len, vector<int>& res) { return min(calMinUp(len, res), calMinDown(len, res)); } }; int main() { int T; cin >> T; vector<int> ans; Solution solve; for (int i = 0; i < T; ++i) { int len; cin >> len; vector<int> temp(len, 0); for (int j = 0; j < len; ++j) { cin >> temp[j]; } ans.push_back(solve.calMinTimes(len, temp)); } for (int i:ans) { cout << i << endl; } return 0; }
商汤科技 FAILED
由于内推直接进入面试。问题基本答上来了,代码写得还不错,莫名其妙地挂了。- 一面
C++虚析构;重载和重写等;编程题是一道链表相关的和归并排序、快速排序。
- 一面
海康威视 offer
笔试题比较基础,编程题也非常简单。- 一面
kernel的移植,一些linux内核态以及用户态相关的知识 - 二面
聊聊天后光速下播 - 三面
算是hr面吧。
- 一面
曾许人间第一流
时间回到2018年,考研后的我没想到研究生生涯会如此凄惨。那时立下的一些志向,许下的愿望大多也都没有实现。因为考研,我度过了非常不开心的两年,同时又因为考研,在校招过程中享受了一些学校学历的红利。原来所有命运赠送的礼物,早已在暗中标好了价格。找工作的不顺利让我接受了自己的平凡,原谅了自己不够聪明。这就是我有血有肉的生活啊,痛苦和眼泪会让体验更加深刻,努力和坚守让自己觉得人生值得,这段时间痛仰版本的阳光总在风雨后让我产生更多共鸣。校招只是漫漫旅途中的短暂一站,接下来还有很多挑战,不过美好人生就在路上。值得庆幸的是,无论怎样变迁,就算是最绝望的时刻,我始终都会想起大一的那个下午,我在键盘上敲下几行代码,那些美妙的字符仿佛在空中飞舞,紧接着LED如霓虹般闪烁,我的眼中也亮了起来,那一刻我恍然大悟,认定了这就是我一生所要追逐的东西。很喜欢kod的一句话,“越努力越幸运”,瑞斯拜。
全部评论
(7) 回帖