这是一份写给在校生的面试准备指南。 适用场景:大三/大四找实习、秋招春招、转正答辩前的基础复习。 不针对某一家公司,也不针对某一个人------所有内容都是可复用的方法与知识地图。
目录
- 面试到底在考什么
- 时间线:什么时候该做什么
- 开场:自我介绍的结构
- HR 高频问题怎么答
- 反问环节:问什么显得专业
- 技术知识地图(核心)
- 项目怎么讲
- 手撕代码:考什么、怎么准备
- 四周复习节奏
- 十个常见坑
一、面试到底在考什么
很多人把面试理解成"背八股"。真正被考察的是三件事,按权重排:
| 维度 | 占比 | 判断标准 |
|---|---|---|
| 基础 | 50% | 概念能不能讲清楚,边界条件知不知道,能不能举一反三 |
| 项目 | 30% | 是不是真做过,难点在哪,你为什么这么选 |
| 表达 | 20% | 有没有条理,被追问时慌不慌,会不会承认"不知道" |
基础题决定下限,项目题决定上限,表达决定面试官愿不愿意给你机会。
一个常见误区:把八股背得滚瓜烂熟,但项目讲不清楚。面试官会认为"这人只会背书"。反过来,基础一般但项目扎实、思路清楚,通过率往往更高。
所以准备顺序应该是:项目 > 基础 > 刷题,但三者不能有短板。
二、时间线:什么时候该做什么
以大三下找暑期实习为例:
| 时间 | 要做什么 |
|---|---|
| 大三上 | 选定方向(后端/客户端/算法),把 C++ 或 Java 基础打牢,写完一个能讲的项目 |
| 大三下 3~4 月 | 投实习,边投边补基础,面试本身是最好的复习 |
| 大三下 6~8 月 | 实习,积累可讲的项目细节 |
| 大四上 8~10 月 | 秋招,实习转正是最高优先级 |
| 大四下 3~4 月 | 春招补录 |
关键判断:如果 3 月还没写完项目,就不要等"准备完美"再投。面试暴露的短板,比闭门复习有效十倍。
三、开场:自我介绍的结构
自我介绍不是复述简历,是给面试官一个提问的钩子。控制在 1~1.5 分钟,四段式:
cpp
① 我是谁:学校 + 专业 + 年级 + 求职方向
② 我会什么:最熟的技术栈,一句话带过(别展开)
③ 我做过什么:1~2 个项目,说清"做了什么 + 结果"
④ 我为什么来:对这个岗位/方向的兴趣
错误示范:
"我叫 XX,来自 XX 大学,学的是 XX 专业,我学过 C++、Java、Python、Go,还学过前端......"
这是在报菜名,面试官记不住任何东西。
正确示范:
"我是 XX 大学计算机专业的大三学生,方向是 C++ 后端。 主要做过两件事:一个是基于 epoll 的高并发 Web 服务器,支持静态资源访问和长连接;另一个是一个小型的 RPC 框架,实现了服务注册和负载均衡。 平时会写技术博客记录学习过程,目前有几十篇原创。 这次投的是后端开发实习,希望能在真实业务里把网络编程这块用起来。"
要点:每句话都留了一个"可追问的接口"。面试官顺着问下去,话题就在你熟悉的领域里。
四、HR 高频问题怎么答
HR 面考的不是对错,是稳定性、动机、情商。下面是高频题和答题原则。
1. 你最大的缺点是什么
原则:真实存在、但不致命的缺点 + 你正在怎么改。
- "我太追求完美了"(假,面试官听腻了)
- "我脾气不好"(真,但致命)
- "我做事情容易钻牛角尖,遇到一个没搞懂的点会卡很久。后来我改成先记录下来、定个时间盒,超过一小时就先跳过,事后统一补。"
2. 你对加班的看法
原则:区分两种情况,表态清楚但不谄媚。
"业务紧急、项目上线这种加班我能接受,也愿意投入。但如果只是白天效率低、晚上补时长,我觉得对团队和自己都不是好事,更希望在正常工作时间内把事做完。"
3. 你对薪资的要求
原则:不报硬数字,把球踢回去,同时表达诚意。
"我对薪资没有硬性要求,相信公司有成熟的薪资体系。作为实习生,我更看重能不能接触到真实业务、能不能有成长,以及有没有转正机会。"
4. 你的职业规划
原则:短期具体,长期有方向,别说"想当管理层"这种空话。
"短期 13 年,把一门语言和一套技术栈吃透,能独立负责一个模块。 中期 35 年,希望能主导一个子系统,带一两个人。 长期看,希望在技术深度上有积累,成为团队里能解决疑难问题的人。"
5. 如果没被录用,你怎么打算
"找工作本来就是双向选择。如果我这次没达到要求,说明我还有短板,我会去补。也希望以后有机会再合作。"
不要说:"那我就去别的公司了"(消极),也不要说"我会很难过"(情绪化)。
6. 你对我们公司了解多少
原则:说一件具体的事,别背官网简介。
"我关注到贵公司在 XX 方向的产品,尤其是 XX 功能,我觉得这个方向和我做的项目有交集,所以想来看看真实工业界是怎么做的。"
准备方法:面试前花 20 分钟,看公司官网 + 最近一个季度的产品更新 + 技术博客。
7. 工作中出现失误造成损失,怎么办
三段式:先止损 → 再担责 → 后复盘 。
"第一,先想办法把损失控制住,该求助就求助。 第二,如果是我的责任,我承担,不推给别人。 第三,事后复盘,是流程问题还是能力问题,避免再犯。"
五、反问环节:问什么显得专业
面试最后"你有什么想问的",不问等于放弃加分。但问错了也会减分。
技术面可以问
- 团队目前主要的技术栈是什么?用什么语言、什么框架?
- 团队现在是在做新功能,还是在维护已有系统?
- 一个新需求从提出到上线,大概经过哪些环节?实习生能参与到哪一步?
- 团队怎么做代码 review 和测试?
- 线上出故障一般怎么排查?有完善的监控和告警吗?
- 您希望实习生在这段时间里成长到什么程度?
HR 面可以问
- 新人培训是怎么安排的?有导师制吗?
- 团队规模大概多少人?分工是怎样的?
- 实习生的转正标准是什么?
- 团队平时怎么沟通?有技术分享会吗?
- 这个岗位未来一年的重点方向是什么?
不要问
- "我这次能过吗"(把面试官架住)
- "加班多吗"(第一轮就问很减分,可以换个问法:"团队的节奏大概是怎样的")
- 官网能查到的信息(显得没做功课)
六、技术知识地图
下面按模块列高频考点。不是让你全背,而是当作检查清单:看到题目能说出 2~3 个要点,就算过关。
6.1 C 语言
static 的三个作用
- 修饰局部变量:生命周期延长到程序结束,但作用域不变
- 修饰全局变量:链接属性从外部变内部,只能本文件访问
- 修饰函数:同上,函数只能本文件调用
指针
指针就是地址,存地址的变量叫指针变量。分类:一级/二级指针、数组指针、函数指针、野指针。被追问再展开,别一上来背一大段。
大小端
- 大端:高位字节存在低地址
- 小端:低位字节存在低地址
- 判断方法:
int a = 1; char *p = (char*)&a;看*p是 1(小端)还是 0(大端)
内存对齐
规则四条:
- 第一个成员偏移为 0
- 其他成员对齐到「对齐数」的整数倍
- 对齐数 = min(编译器默认对齐数, 成员大小),VS 默认 8
- 结构体总大小是最⼤对齐数的整数倍
原因:平台可移植性(有些硬件只能访问特定地址)+ 性能(不对齐要访问两次)。
编译到运行的四阶段
cpp
预处理(.i)→ 编译(.s)→ 汇编(.o)→ 链接(可执行)
volatile
告诉编译器"这个变量可能被意外改变",禁止优化,每次都从内存读。用于硬件寄存器、多线程共享变量、信号处理函数。
#define 和 const
#define 是预处理文本替换,无类型检查;const 有类型检查、可调试。C++ 里优先用 const / constexpr。
指针常量和常量指针
cpp
const int* p; // 指向常量的指针,值不能改,指针能改
int* const p; // 指针常量,指针不能改,值能改
const int* const p; // 都不能改
口诀:const 在 * 左边修饰值,在 * 右边修饰指针。
malloc / calloc / realloc
malloc:分配,不初始化calloc:分配 + 清零realloc:调整已分配内存大小,可能搬家
6.2 C++
C 和 C++ 的区别
C 是面向过程,C++ 支持面向对象(封装/继承/多态)、模板、异常、引用、命名空间、STL。
引用 vs 指针
| 引用 | 指针 | |
|---|---|---|
| 是否可为空 | 否 | 是 |
| 是否可改绑 | 否 | 是 |
| 是否占内存 | 一般无独立空间 | 占 |
| 底层实现 | 通常是指针 | 指针 |
inline
建议编译器展开,减少函数调用开销。适合短小、频繁调用的函数。虚函数、递归函数不适合内联。
空类的大小
1 字节。编译器要保证不同对象有不同地址。
this 指针
- 隐含在非静态成员函数中,指向当前对象
- 存在栈上(通常通过寄存器传递)
- 不能为空,为空时访问成员就是未定义行为
不能重载的运算符
. :: ?: sizeof . *
必须用初始化列表的成员
const 成员、引用成员、没有默认构造函数的类类型成员、基类(如果基类没有默认构造)。
explicit
禁止隐式类型转换。单参数构造函数不加 explicit,可能被意外调用。
static 成员
属于类不属于对象,所有对象共享,类外定义。
new/delete vs malloc/free
| new/delete | malloc/free | |
|---|---|---|
| 是否调构造/析构 | 是 | 否 |
| 返回类型 | 具体类型指针 | void* |
| 失败行为 | 抛异常 | 返回 NULL |
| 是否可重载 | 是 | 否 |
new 底层:operator new 分配内存 → 调构造函数。
模板与特化
泛型编程的核心,编译期生成代码。特化用于对特定类型做特殊处理,写法:template<> + 空尖括号 + 指定类型。
vector 扩容
按倍数扩容(通常 1.5 或 2 倍),扩容时重新分配 + 拷贝 + 释放旧空间。所以 reserve 能减少扩容次数。
迭代器失效
vector:扩容后所有迭代器失效;erase后当前位置及之后失效list:只有被删元素的迭代器失效vector vs list
vector 连续内存、随机访问 O(1)、插入删除 O(n);list 非连续、随机访问 O(n)、插入删除 O(1)。
继承与菱形继承
多继承可能产生数据冗余和二义性,用虚继承解决(共享一份基类数据)。
多态三条件
- 继承体系
- 基类指针/引用调用
- 虚函数 + 派生类重写
多态原理
虚函数表 + 虚表指针。每个有虚函数的类有一张虚表,对象里有一个 vptr 指向它,调用时通过虚表找到实际函数地址。这就是动态绑定。
为什么析构函数要虚
基类指针 delete 派生类对象时,如果析构不是虚函数,只会调基类析构,派生类资源泄漏。
C++11 重要特性
auto、nullptr、范围 for、右值引用与移动语义、std::function/lambda、智能指针(unique_ptr/shared_ptr/weak_ptr)、override/final、constexpr、线程库。
单例模式
cpp
class Singleton {
public:
static Singleton& getInstance() {
static Singleton instance; // C++11 起线程安全
return instance;
}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
private:
Singleton() = default;
};
四种类型转换
static_cast(普通转换)、dynamic_cast(多态类型安全转换)、const_cast(去 const)、reinterpret_cast(底层重解释)。
6.3 操作系统 / Linux
进程 vs 线程
进程是资源分配单位,线程是 CPU 调度单位。同一进程的线程共享地址空间、文件描述符,但各有栈和寄存器。
进程状态
创建、就绪、运行、阻塞、终止。
fork
创建子进程,子进程复制父进程的地址空间(写时复制)。返回值:父进程得子进程 PID,子进程得 0,失败返回 -1。
僵尸进程 / 孤儿进程
- 僵尸:子进程结束但父进程没
wait,占着进程表项- 孤儿:父进程先死,子进程被 init/systemd 收养
地址空间
每个进程有独立的虚拟地址空间,通过页表映射到物理内存。好处:隔离、方便管理、支持超过物理内存的程序。
文件系统
文件 = inode + 数据块。inode 存元信息(权限、大小、块指针),目录是"文件名 → inode 号"的映射。
动静态库
- 静态库(
.a):链接时拷进可执行文件,体积大,部署简单- 动态库(
.so):运行时加载,多个程序共享,节省内存
IPC 方式
管道、命名管道、消息队列、共享内存、信号量、信号、socket、文件。
线程同步
互斥锁、条件变量、信号量、读写锁、原子操作。
死锁四条件
互斥、持有并等待、不可剥夺、循环等待。破坏任意一个即可避免。
用户态 / 内核态
内核态可执行特权指令、访问全部内存;用户态受限。切换方式:系统调用、中断、异常。
6.4 计算机网络
TCP vs UDP
| TCP | UDP | |
|---|---|---|
| 连接 | 面向连接 | 无连接 |
| 可靠性 | 可靠(重传、确认) | 不可靠 |
| 传输方式 | 字节流 | 数据报 |
| 效率 | 低 | 高 |
| 场景 | HTTP、文件传输 | 视频、DNS、游戏 |
三次握手 / 四次挥手
- 握手:SYN → SYN+ACK → ACK(确认双方收发能力)
- 挥手:FIN → ACK → FIN → ACK(双方各发一次,所以四次)
TCP 可靠性
序号 + 确认 + 超时重传 + 校验和 + 流量控制(滑动窗口)+ 拥塞控制(慢启动、拥塞避免、快重传、快恢复)。
粘包
TCP 是字节流,没有消息边界。解决:定长、分隔符、长度字段(最常用)。
HTTP vs HTTPS
HTTPS = HTTP + TLS,加密传输、身份认证、防篡改,默认 443。
Cookie / Session / Token
- Cookie:存在客户端
- Session:存在服务端,靠 Cookie 里的 sessionid 关联
- Token:客户端持有,服务端验签,无状态
select / poll / epoll
| select | poll | epoll | |
|---|---|---|---|
| 最大连接数 | 有上限(1024) | 无上限 | 无上限 |
| 效率 | O(n) 轮询 | O(n) 轮询 | O(1) 事件通知 |
| 拷贝 | 每次全量拷贝 | 每次全量拷贝 | 内核维护,mmap |
epoll 适合大量连接但活跃比例低的场景。
LT / ET
- 水平触发:只要缓冲区有数据就一直通知,可以阻塞读
- 边缘触发:只在状态变化时通知一次,必须非阻塞读 + 循环读到 EAGAIN,否则数据会丢
从输入 URL 到页面显示
DNS 解析 → TCP 连接 → 发 HTTP 请求 → 服务端处理返回 → 浏览器渲染 → 断开连接。
6.5 数据库
InnoDB vs MyISAM
| InnoDB | MyISAM | |
|---|---|---|
| 事务 | 支持 | 不支持 |
| 外键 | 支持 | 不支持 |
| 锁 | 行锁 | 表锁 |
| 索引 | 聚簇索引 | 非聚簇 |
B 树 vs B+ 树
B+ 树只有叶子节点存数据、叶子节点之间用链表连接、非叶子节点只存索引。所以:查询更稳定、范围查询更快、磁盘 IO 更少。MySQL 用 B+ 树做索引。
事务 ACID
原子性(undo log)、一致性、隔离性(锁 + MVCC)、持久性(redo log)。
隔离级别
读未提交、读已提交、可重复读(MySQL 默认)、串行化。
MVCC
靠三个隐藏字段(事务 ID、回滚指针、隐藏主键)+ undo log + read view 实现。让读不加锁也能保证一致性。
索引失效的场景
like '%xx'、对索引列做函数运算、隐式类型转换、or 连接非索引列、!=、联合索引不满足最左前缀。
6.6 数据结构与算法
排序复杂度速查
| 算法 | 平均 | 最坏 | 空间 | 稳定 |
|---|---|---|---|---|
| 快排 | O(nlogn) | O(n²) | O(logn) | 否 |
| 归并 | O(nlogn) | O(nlogn) | O(n) | 是 |
| 堆排 | O(nlogn) | O(nlogn) | O(1) | 否 |
| 冒泡 | O(n²) | O(n²) | O(1) | 是 |
| 插入 | O(n²) | O(n²) | O(1) | 是 |
常考手撕
链表反转/判环/合并、二叉树遍历(递归+非递归)、快排/归并、堆排序、字符串处理、LRU、线程池、单例。
6.7 分布式与中间件(了解即可)
校招对这块要求不深,能讲清楚概念 + 一个使用场景就够:
- 缓存三兄弟:穿透(查不存在的数据,布隆过滤器)、击穿(热点 key 过期,加互斥锁)、雪崩(大量 key 同时过期,过期时间加随机)
- 消息队列:解耦、削峰、异步
- Kafka:分区、副本、持久化,高吞吐
- RPC vs HTTP:RPC 二进制编码(protobuf)、性能高、适合内部服务调用
- 负载均衡:轮询、加权、一致性哈希
- 容器化:Docker 镜像 + 容器,解决环境一致性问题
七、项目怎么讲
面试官问项目,本质在验证三件事:是不是你做的、难在哪、你怎么想的 。
三层结构
cpp
① 是什么:一句话说清楚项目解决什么问题
② 难点:选 1~2 个技术难点,讲清楚"为什么难 + 你怎么解决"
③ 结果:量化,比如 QPS、延迟、代码量、上线情况
反例
"我做了一个 Web 服务器,用了 epoll 和线程池,支持 HTTP 协议。"
------这是功能列表,不是项目。任何人都能说。
正例
"这个服务器主要解决大量并发连接下的 IO 效率问题。 最初的版本用阻塞 IO,压测到 200 连接就开始卡,因为每个连接占一个线程,上下文切换成本太高。 后来改成 epoll + 边缘触发 + 线程池,每个线程用非阻塞方式处理就绪事件。这里踩了个坑:ET 模式下如果一次没读完,剩下数据不会再通知,所以必须循环读到 EAGAIN。 改完之后单机压测能到 8000+ QPS,连接数上万。"
准备清单
面试前把项目想清楚这几个问题:
- 为什么要做这个项目?解决什么问题?
- 整体架构是什么?画得出来吗?
- 最难的一个点是什么?怎么解决的?
- 如果重做一遍,你会改哪里?
- 这个项目的瓶颈在哪?
第 4、5 题最容易被问到,也最能体现思考深度。
八、手撕代码:考什么、怎么准备
常见题型
| 类型 | 代表题 |
|---|---|
| 链表 | 反转、判环、合并、删除倒数第 N 个 |
| 二叉树 | 三种遍历(递归+非递归)、层序、深度 |
| 排序 | 快排、归并、堆排 |
| 字符串 | 反转、回文、KMP |
| 设计题 | LRU、线程池、单例、生产者消费者 |
| 算法 | 二分、双指针、滑动窗口、DFS/BFS |
准备方法
- 手写,不要用 IDE 补全,面试是白板或纯文本编辑器
- 先讲思路再写,面试官更看重思路
- 边界条件主动说:空指针、单元素、越界
- 写完自己走一遍例子
常被忽略的点
- 头文件写了吗
- 变量初始化了吗
- 内存释放了吗(C/C++)
- 异常情况怎么处理
九、四周复习节奏
假设每天能投入 4 小时:
| 周 | 重点 | 具体安排 |
|---|---|---|
| 第 1 周 | 语言基础 | C/C++ 语法、关键字、面向对象、STL 源码 |
| 第 2 周 | 系统与网络 | 进程线程、内存、IO 模型、TCP/IP、HTTP |
| 第 3 周 | 数据库 + 项目 | 索引、事务、SQL 优化;把项目讲稿写出来 |
| 第 4 周 | 刷题 + 模拟 | 每天 3 道手撕,找同学模拟面试 |
每天固定动作 :30 分钟复盘昨天不会的题,写进笔记。不会的题一定要写下来,反复看。
十、十个常见坑
- 背答案不理解。面试官换个问法就露馅。每道题想清楚"为什么"。
- 项目说不清难点。只说功能不说取舍,等于没做过。
- 遇到不会的硬编。诚实说"这块我不太熟,我的理解是......",比编造好得多。
- 简历写太多技术栈。写了就要能扛住追问,没把握的删掉。
- 不问问题。反问环节放弃,等于少一次展示动机的机会。
- 只说结论不说过程。面试官要听的是你的思考路径。
- 不会估算复杂度。任何方案都要能说出时间/空间复杂度。
- 忽视基础课。操作系统、计算机网络这两门,几乎是所有后端的必考区。
- 面试完不复盘。每次面完记下被问倒的题,下一场就能补上。
- 只准备一家。多投多面,面试状态是练出来的。
写在最后
面试本质是一次结构化的技术对话:你证明自己会什么,对方判断你能不能干活。
八股是入场券,项目是加分项,表达是放大器。三者缺一不可,但最容易被忽略的是最后一项------同样的知识,讲得清楚和讲得含糊,结果完全不同。
准备的过程中,把每道题都当成"如果我是面试官,我想听到什么",方向就不会错。