C++开发 9 年,目前人在大厂,做 C++ 相关的开发,作为资深 C++ 面试官,我来聊聊面试官眼中的校招简历中的 C++ 项目吧,希望对各位学弟学妹有帮助。
1. 简历中如何介绍自己的项目?
从面试官的角度来说,我们也是从学生时代过来的,对于大多数学生做的项目的水平都心知肚明。
对于应届生,项目其实不是必需的,面试官更看重的还是基础知识部分;倘若某位同学在简历中描述了自己的项目,那么面试的时候,面试官会结合项目的内容考察一下。
为了让大家可以感同身受,我截取了两份真实应届生求职 C++ 岗位的简历中项目描述,你可以自己先想一想,如果在简历中这样描述项目,面试时可能会被问到哪些问题。
1.1 A 同学的项目
看到 A 同学的项目描述了,我在实际面试中问了该同学以下问题:
-
介绍一下整个服务的程序结构。该同学的描述是主线程开启监听 socket 之后,进入无限循环调用 accept 处理客户端连接,accept 返回新的客户端 socket 后封装成任务交给线程池处理,线程池的线程共用一个队列,当有任务产生时,从任务队列中取出任务执行。我首先询问了一下,主线程如何通知工作线程有任务,该同学回答说:使用条件变量,并且每次只唤醒一个工作线程,此时我扩展了一下问题,假设我某次投递了 N 个任务,我想同时唤醒 N 个线程(N 小于工作线程数目),这样要如何设计?
-
接着我对他关于程序结构的描述提出了质疑,如果 accept 之后就将客户端 socket 封装成任务交给线程池处理,此时严格来说是没有任务需要执行的,因为客户端 socket 上不一定有数据需要收发,如果有数据需要收发,任务线程如何处理?如果在工作线程中将客户端 socket 挂载到某 IO 复用函数上去,那么为了保证效率,这些任务就常驻线程池了,这样几个连接之后,线程池的所有线程都被占用了,无法继续处理其他任务了。在我提出这些质疑后,这位同学给不出合理的解释。
-
既然是 Web 服务,那么解析 HTTP 数据包是一个核心功能,我询问了该同学 HTTP 协议的格式,该同学清楚 HTTP 包头和包体如何分界,当时当我问到,HTTP 是基于 TCP 协议的,TCP 是流式协议,包头可以通过 \r\n\r\n 确定边界,包体如何确定边界呢?该同学说不清楚。我接着又问,既然是 HTTP 协议,那么肯定可以处理 GET 和 POST 请求,那么 GET 请求和 POST 请求有什么区别,你在处理的时候,如何区分的,分别又是如何解包的,该同学只能说出 GET 请求的参数放在 URL 后面,但是说不清楚 POST 请求的数据放在哪里,如何确定数据长度。这里实际是对 HTTP 协议格式的考察。
-
项目中说这个服务是“高性能的”,可以支持到上万 QPS,我接着问,你是如何做压测的,压测的服务所在的配置如何,最后该同学告诉我,该项目其实只是单纯的支持上万连接,而不是上万 QPS,谈到连接,我让该同学介绍下 Socket 编程服务端和客户端的基本流程,该同学答出来了,接着我问了一个细节问题,服务端需要调用 bind 一个端口号,如果不 bind 会怎样?客户端通常不需要 bind 一个端口号,但是如果调用 connect 函数前,我们调用 bind 函数绑定一个端口号,会怎样?该同学答不上来。
-
项目中提到 Proactor 模式,我询问他这是一种什么样的模式。然后,我说据我所知,一般 Reactor 模式用得更多,那你知道 Proactor 和 Reactor 模式有什么区别吗?该同学答不上来。在解释 Proactor 模式的时候,该同学提到了 IO 复用函数,我询问他知道哪些常用的 IO 复用函数,该同学介绍了 select、poll、epoll,我接着询问这三个 IO 复用函数的使用场景和优缺点,该同学答对后,我问了下 epoll 模型的水平和边缘触发模式的区别。接着,我给出一个具体场景,假设我某个客户端 socket 绑定到 epollfd 上后使用边缘触发模式,现在该客户端发来了 100 个字节,是否会触发读事件;服务端收了 50 个字节,读事件会在下一轮中继续触发吗?假设接着客户端又发了 10 字节,此时服务端会触发读事件吗?
-
我接着询问了该同学使用何种 IDE 开发的该项目,于是问了一些该 IDE 的调试命令,该同学不熟悉。
-
接着我问该同学是否熟悉一些网络调试命令,如如何查看一个服务监听端口已经开启、当前连接信息,是否会使用 tcpdump 等抓包工具等等。
通过面试,我断定这位同学并没有深度参与这个项目,可能只是把别人的东西拿来借用一下.另外,可以看出来该同学不熟悉 HTTP 协议,网络编程的基础勉强及格,实践(调试)经验不足。所以,这个项目写在简历中起到了相反的作用。面试是不通过的。
1.2 B 同学的项目
不知道你能否看出该项目描述的问题?
B 同学的项目描述的问题在于太多的业务描述,关于技术的描述(仅有 C++、算法等字样)太少了,这样给面试官问问题的空间就大了,很容易问到面试者不会的地方。另外,该项目也无法看出面试者在项目中通过何种技术解决问题的能力。
1.3 在简历中描述自己的项目的几点建议
关于项目描述,给同学们有如下几点建议:
-
项目经验不是最重要的,不会要求一个应届生有多少的项目经验,最重要的还是基本功;
-
如果有项目,一定要真实合理。所谓真实是你写到简历中的项目一定是真实的,即你参与或者熟悉的,不反对包装开源项目作为自己的项目,但一定要把项目的核心内容和程序的核心原理细节搞清楚,让面试官相信你真的参与了这个项目。举个例子,如果你的项目是 Web 服务器,那么如果你连 HTTP 协议格式都不清楚,面试官如何相信这个 Web 服务器是你自己做的呢?如果你的项目中使用了 protobuf 协议,但你连 protobuf 如何做到协议兼容和扩展都不清楚,那把这样的项目写到简历中,意义就不大。所谓合理是说,项目的技术实现描述要合理,比如上文中支持万级 QPS 而不给出具体硬件配置,显然不合理的;说自己调试过项目,连基本调试命令都说不清楚,显然难以自圆其说。
-
如果岗位工作和你的项目内容属于不同业务,业务介绍尽量简洁,多从技术的角度来写,不要罗列太多的技术术语和技术栈,把范围缩小,一般列举 1~3 个熟悉的技术栈或者想引导面试官考察你的内容即可,尤其不要写不相关的内容,否则面试被问到又答不出来会非常尴尬。有些同学在项目中写了非常多的技术术语和依赖件,如 Redis、MySQL、Kafka,那么这样很容易给自己挖坑,例如面试官可能会真的问你 Redis 和 Kafka 的一些内容,对于应届生的话,想答出来有一点困难。
2. 真的,应届生主要看基础
对于应届生来说,面试的要求基本就一句话:要求基础扎实。
那何为“基础扎实”呢?先说基础,基础指的是计算机相关专业的各个专业课程内容,“基础扎实”指的是对这些专业课内容有较好的理解和实践。计算机相关专业课一般包括算法数据结构、编程语言、操作系统原理、计算机网络、数据库等,当然,有些岗位因为特殊需要,可能还会进一步对汇编、编译原理等知识进行考察。下文我会逐一介绍在面试中会考察这些基础课程的哪些内容。
3. 算法和数据结构如何准备?
先介绍应届生如何准备算法和数据结构。
算法和数据结构是整个面试中的重中之重,尤其像 BATJ 这样的一二线大厂,在面试考察内容中一般会占到百分之五十的比重(参见上文图中红色部分)。对于应届生或者实习岗位的招聘来说,如果算法题做得不好,基本上会被一票否决。而且与有多年工作经验的社招相比,一般对应届生的算法能力要求更高。举个例子,编写一个对折链表的算法,对于工作多年的社招可能写个大概就可以了,但是对于应届生必须要求完整的写出来。
有的公司在进入人工面试之前,会安排专门的笔试题来考察算法和数据结构,如果笔试过不了,基本不会安排下一轮人工面试;即使进入了人工面试,面试官也会去看面试的同学的笔试成绩和答题内容状况。
包括算法和数据结构在内的所有基础知识,重在平时的积累,而不是面试的时候临时抱佛脚。部分同学直到校招临近才开始准备,但是因为需要准备的内容实在太多而不知所措,这里有个实战策略推荐给你,策略的原则是花最短的时间准备更加重要的面试考察内容:
-
如果你的时间充足,好好准备算法数据结构和其他基础知识;
-
如果你临近面试或者时间比较紧迫,想要面试大厂(一二线公司),优先准备算法数据结构;
-
如果你临近面试或者时间比较紧迫,想先找个做 C++ 开发的小公司安顿下来,优先准备其他基础知识(即上文中提到的编程语言、操作系统原理、计算机网络、数据库等内容)。
老实说,算法和数据结构的内容挺多的,我知道很多同学会刷算法题,有一些同学专门挑一些 hard 级别的算法题来刷。这里可以告诉大家,刷一定的算法题是有必要的,但是过多刷 hard 级别的题目是没必要的,不仅浪费时间且收益不大(算法岗位除外)。适当刷题的目的是保持对常见的算法题的熟悉度,算法题一般分为两类,一类是算法书或者 leetcode 上的原题,面试中考察的这类算法题一般都集中在数组、链表、哈希表等常见数据结构上,所以大家准备的时候,也可以以这类算法题为主,对于像红黑树、图等一般不会考察的;另一类是开放性思维题或者场景设计题,举两个例子,在时间和空间算法复杂度有一定要求的情况下,从朋友圈广告(亿级别)中找到最受欢迎(点击量)最高的广告,一只青蛙往楼梯上跳跃,每次可以跳一阶二阶三阶,对于 n 阶楼梯,计算青蛙有多少种跳跃方式。
4. 其他基础知识会考察哪些问题?
只要是技术面试,算法和数据结构通常都会考察。
由于本文主题是 C++ 面试,下面我们来说 C++ 部分,C++ 是一门很注重语言和接口背后原理的编程语言,下面我分享一下我作为 C++ 面试官常问的一些问题。
4.1 C++ 语言篇
考察 C++,不仅仅指 C++,也可能包含部分 C 语言的内容。
-
请说出 const char *p 、 char const *p 与 char * const p的区别,这个问题答错,面试基本不会通过,说明基本功差。
-
static_cast、dynamic_cast、reinterpret_cast 和 const_cast 几种 C++ 类型转换符的区别偶尔也会问到,面试者至少要知道第一种。
-
看下面代码,计算各个值(这个问题答错,面试基本不会通过,说明基本功差):
// 32位系统上
struct A {
int i;
char j;
};
A a;
A* pa = &a;
sizeof(a) = ?
sizeof(pa) = ?
int arr[8];
int* parr = arr;
sizeof(arr) = ?
sizeof(arr[0]) = ?
sizeof(parr) = ?
char sz[] = "helloworld";
char* psz = sz;
sizeof("helloworld") = ?
sizeof(sz) = ?
sizeof(psz) = ?
-
关于 C++ 面向对象,问得最多的问题是 C++ 的多态,一般会问多态的概念和多态在 C++ 语言中的实现,如果面试者说出了多态用途并引出了类的虚表,接下来面试官可能会追问虚表的结构。一个常见的问题是“菱形继承”中虚表的个数,即类 A 有子类 B 和 C,类 D 继承 B 和 C,那类 D 有几个虚表呢?
-
有些面试官会以面向对象的三大特性开始提问,面试者回答出来“封装”、“继承”和“多态”后,注意了,接下来面试官可能要开始考察“继承”和“多态”(多态上文介绍了,不再重复)。常见的继承问题有,我们知道 C++ 的类继承分为 public、protected、private 三类继承方式,面试官会询问如果类 B 有一个 public/protected/private 的方法 f,现在类 A 以 public/protected/private 方式继承,那么在 B 中方法 f 是何种访问权限。此类问题答错,面试基本不会通过。
-
考察继承的时候也会问一些父子类的构造和析构顺序或者子类析构函数为何要用 virtual 关键字声明的简单问题。这类问题不问则罢,如果被问到,答错,面试必挂。
-
继承问题中还有常常会问重载(override)和重写(overwrite)的区别。关于重载有两个常见的问题,第一个:一个类方法名和参数数量、类型和顺序都是一样的,但是返回值类型不一样,是否构成重载?这个问题比较简单;第二个问题,一个方法加了 const 和不加 const 是否构成重载?答案是构成重载的,可以自己试一试。
-
C++ 也会对常见的 stl 容器进行考察,例如一般问你是否用过 std::map、std::set 等数据结构,如果你说没用过,那面试到此就基本结束了(面试不通过);如果你说用过,接下来会问你这些容器背后的数据结构、插入删除的算法复杂度(最好和最坏的情况)、以及各个容器的使用场景和注意事项。有的面试官还会问,std::vector 的 resize 和 reserve 方法的区别。
-
如果岗位要求会使用 C++11 或者面试者的简历中提到 C++11 等 Modern C++ 的内容,面试官会问一些 C++11 的内容,通常会问你用过或者熟悉哪些 C++11 的特性,我看部分同学只能简单地说出 auto 关键字等少量语法特性,这样显然是不行的。C++11 问得最多的是几种常用的智能指针,即 std::unique_ptr、std::shared_ptr、std::weak_ptr,面试者需要熟悉这几种智能指针的使用场景和区别,以及智能指针背后的实现原理(引用计数),智能指针还会结合多线程场景来问,例如智能指针是否是线程安全的(引用计数是线程安全的,管理的内容不是)。左值、右值、左值引用、右值引用、std::move、std::emplace 和移动构造函数的内容也是 C++11 面试常问的问题。
-
读者可以看下面的面试题是够能做出来:
#include <memory>
class A {
};
void f1(std::unique_ptr<A>&& a1) {
}
void f2(std::unique_ptr<A>&& a2) {
//这里编译有错误,如何修改?
f1(a2);
}
int main()
{
std::unique_ptr<A> spA(new A());
//这里编译有错误,如何修改?
f2(spA);
return 0;
}
-
如果面试的岗位对底层要求有一定的了解,会结合 C 语言考察一些内容,例如函数的调用方式。面试官可能不会一上来就直接告诉你他在考察函数调用的知识点,他可能会结合一些案例来问,例如,假设让你实现 C 语言中的 printf 函数,需要注意哪些事项?一般有两点,第一点支持不定项参数,这个如果看过相关源码的同学,可能知道使用 va_ 系列的宏就可以了,另外一点是这种支持不定参数的函数的调用方式必须是 C 调用(__cdecl),不能是标准调用(__stdcall),因为 C 调用由调用者来平衡堆栈,标准调用由被调用者来平衡堆栈,对于支持不定参数的函数,只有实际被调用时,才知道要平衡多长的堆栈,因此如果使用标准调用,编译器不知道如何为该函数生成平衡堆栈的指令。如果你答得不错,面试官可能会深入下去,考察你栈的结构,各个变量和函数参数在栈中的分布。考察 C++ 中无法返回一个局部变量的地址或引用的原因也是对栈结构知识的考察,当然,可能从栈又会引出堆的概念和用法,这就属于操作系统原理的知识了。
-
C++ 中的 __thiscall 有时候也会在考察范围内,比如面试官问 C++ 的类实例方法和静态方法如何实现的,给一个类增加一个静态/实例方法是否会增加类的大小,给一个类增加一个静态/实例字段是否会增加类的大小,sizeof 一个无任何方法和成员的空类等于多少,为什么不是 0?
4.2 操作系统原理篇
操作系统原理考察的内容一般比 C++ 语言考察的内容和范围要广,但是在面试官问了几个问题之后,如果面试者答得不好,面试官就会换其他方向问了。
-
操作系统原理问的最多的是多线程的问题或者叫资源同步或者资源竞态或者锁的问题。最常见的问题如进程和线程的区别,线程同步有哪些技术,一般面试官会期望你能说出几种,在 Windows 操作系统上常用的有 CriticalSection、Event、Semaphore、Mutex,在 Linux 操作系统上有 mutex、semphore、read-write-lock、condition-variable,面试者需要知道这几种对象分别用于什么场景,简单来说,mutex 可以将一大片代码段进行加锁,对于当前线程可以阻塞也可以递归(递归锁),对于其他线程直接阻塞,semphore 用于资源有计数需求的精准控制,读写锁用于有读有写的场景,条件变量用于条件不成熟时的阻塞和条件成熟时的主动唤醒。读写锁和条件变量是问的最多的两个对象,面试者需要知道读写锁读与写竞争的四种情形,以及如何写出正确使用条件变量的代码,我的建议是实际写一写,搞明白就不会忘记了,另外,条件变量的虚假唤醒概念也是经常被问到的。有的同学说不出来,上来就说分布式锁,这里考察的的一般是进程内的多线程之间的锁,分布式锁指的是多个进程之间的锁,建议千万不要在面试的时候信口开河。
-
进程之间的通信方式也是经常考察的知识点,常见的方法有编译器的共享段、共享内存、管道、socket 等,当然,Windows 上还有油槽(mailslot),Linux 和 Windows 都有的剪贴板都是进程共享的常见方法。
-
另外,关于进程的考察,面试者还需要掌握 Linux 孤儿进程、守护进程、僵尸进程等概念,知道 fork 和 exec 调用的区别。
-
面试中也可能会考察一些编译链接的知识,如一个 .cpp 文件变成可执行程序会经历哪些阶段,静态变量、全局变量(初始化的和未初始化的)分别放在可执行文件的什么位置,映射到进程地址空间中之后,又放在什么位置。
-
逻辑地址、虚拟地址和物理地址的区别,同样的道理,面试官也可能会从一个小问题慢慢深入,例如代码中 int i = 0; 中变量 i 的地址属于哪种地址,什么是实模式和保护模式等等,堆和栈在内存地址空间的分布和增长方向。
-
什么是内存映射文件,何时使用内存映射文件。
-
动态链接库和静态链接库的区别是什么?如何生成动(静)态链接库。
4.3 计算机网络
对于应届生的话只会问一些计算机网络的理论知识,但是如果面试者的项目中有相关的网络编程的内容,也会问一些网络编程的内容。我们在这个章节只列举经常考察的计算机网络理论知识,更多的网络编程的内容,我们放到项目经验这一节来详细介绍。
常考察的计算机网络知识有:
-
三次握手和四次挥手的过程,面试的时候如果被问到这个问题,一定要说得清楚,当然不必说出每一步动作之后具体的网络状态,但是一些关键的状态如 CLOSE_WAIT 和 TIME_WAIT 状态要说出来。这个其实不难,但是也容易忘记,所以要面试前好好准备一下,如果因为这类题目如果答不出来面试不通过,实在可惜。当然,有的面试官可能不会直接问你三次握手四次挥手,而是结合一些实际场景来问,例如,CLOSE_WAIT 状态一般出现在网络通信的客户端还是服务端,某服务出现大量 TIME_WAIT 状态应该如何解决,此时面试者要能够回忆出四次挥手的过程,能够分析出原因并给出答案。
-
再者,网络是如何分层的,理论上是几层,实际实现又是几层,TCP/UDP 的区别和使用场景等等。
-
关于 TCP,滑动窗口的概念、流量控制与拥塞控制、糊涂窗口综合症、TCP 是如何保证通信的可靠性的都是常考察应届生的问题。
-
常见的协议格式的考察,问得最多的就是 HTTP 协议了。
更详细的内容请接着看项目篇。
4.4 数据库
一般后端岗位对数据库有一定的要求,但是作为加分项,通常不会作为强行要求。
对于应届生,只要掌握数据库的增删改查、知道索引的概念和实现原理即可,例如你要知道索引的常见实现是 B+ 树,索引的结构,以及如何建立有效索引;另外一点是数据库的事务,面试者只要将事务的概念讲清楚即可。
5. 注意开场的自我介绍部分
面试的开场通常是面试官让面试者做一下自我介绍。这个自我介绍比较有讲究,但是大多数同学的自我介绍都平平无奇,基本上是,我叫 XXX,毕业于 XXX 院校 XXX 专业等一些客观信息的介绍。
自我介绍其实是很讲究技巧的,如果把握得当,可以引导面试讨论内容的方向,掌握主动权,把面试考察内容往自己擅长的方向引导,所以建议即将参加面试的同学可以按照本文的建议提前演练一下。例如你可以在自我介绍中主动介绍自己的特长、感兴趣的技术点,平常在该方面花了不少时间,或者自己曾经做个 XXX 项目,通过 XXX 技术解决了 XXX 问题。面试官大概率会就你说的这些点追问,这样就把面试话题引导到你擅长的方向了。
下面是两个模板:
自我介绍模板 1
面试官你好,我叫 XXX,即将毕业于 XXX 大学,专业是 XXX,我比较擅长算法和数据结构,在校期间参加过多次 XXX 比赛,并取得了 XXX 成绩,我平常在算法上花的时间比较多,研究过很多开源项目中的算法,如 Linux 内核中的 LRU 算法和进程调度算法等,在个人博客/站点发表过 n 篇高质量算法和数据结构的博文。
自我介绍模板 2
面试官你好,我叫 XXX,即将毕业于 XXX 大学,专业是 XXX,我比较喜欢网络编程,在校期间花过大量时间在网络编程上面,我通读过《TCP/IP 协议》三卷等经典计算机网络书籍,研究并梳理过常见 C/C++ 网络库的结构和运行机理,了解很多网络编程的细节和注意事项,曾经是我校 XXX 项目的底层网络模块的主要开发者,在 github 发布过 XXX 网络库项目,fork 多少,start 多少。
6、可以从哪里可以学到上述知识?
这里推荐零声教育的课程,不了解的可以先来看看下面的录播视频,内容较多,比较全面,可以收藏起来慢慢看
2024年c/c++程序员如何提升自己的核心竞争力?这套linux c/c++后端服务器开发技术教程不要错过!https://www.bilibili.com/video/BV1CF4m1L7hU/
正在准备面试的小伙伴,给大家分享一份面试真题,1000道大厂技术面试题,需要的小伙伴可以扫描二维码加贝贝老师领取(备注123)
最后,一个完整的linux c/c++开发学习路线图分享给大家(摘自零声教育课程大纲,直接对标腾讯T8技术栈)
网页版:Linux C/C++开发(后端/音视频/游戏/嵌入式/高性能网络/存储/基础架构/安全)成长体系课程
内容太多,详细知识点请看网页版