2024年秋招季,百度第二批笔试我实际参加了一下,C++/PHP/GO工程师这条线,整体感受是:题型覆盖面广、语言细节考得深、算法题不白给。这篇文章不聊虚的,直接把笔试中呈现出来的考察逻辑、核心考点、我做题时的思路和复盘心得整理出来,给后面要参加同类笔试的朋友做参考。
先说清楚这篇文章适合谁看:如果你是准备投递大厂后端开发岗位,尤其是C++、PHP、GO这三个语言方向之一的应届生或转行者,这篇文章能帮你快速了解“笔试到底考什么、该怎么复习”。如果你已经有一定基础,也可以直接跳到第二、三、四部分对照检查自己的知识盲区。
1. 笔试整体风格与考察逻辑
1.1 从题型分布看考察范围
百度这批笔试的题型大致分为两类:选择题(含多选)和编程题。选择题覆盖计算机基础知识、语言特性、网络、操作系统、数据库等;编程题通常是2~3道,考察算法设计与编码实现能力。整体时间压力比较大,编程题如果思路卡住,很难在限定时间内全部AC。
从热词数据里可以看到,大家搜索最多的是“C++小游戏”“php经典程序100题”“go面试题”“vscode配置c/c++环境”这类关键词,这说明很多人还在纠结"怎么学"的阶段,但对笔试来说,语言语法只是基本功,真正拉开差距的是对语言底层机制的理解深度和算法反应的熟练度。
1.2 为什么一次性招三种语言
百度很多部门的后端技术栈并不统一,有的用C++处理高并发底层服务,有的用PHP快速迭代业务逻辑,而Go在高性能微服务和云原生方向上用得越来越多。笔试统一出题、按语言分组考察,是为了筛选出基础扎实、能够快速上手任意语言的人。这就意味着,你报的志愿语言只是侧重点,计算机通用基础反而是大头。
我有一种很强烈的感觉,这套笔试卷子的设计者希望找到的候选人,不是“只会刷LeetCode的选手”,而是既懂语言特性又能写对业务代码的人。所以复习时不要只刷题,语言层面的八股和工程实践也必须兼顾。
1.3 时间分配策略
笔试时长一般是90分钟到120分钟。选择题如果一道题卡超过两分钟,先标记跳过;编程题先做自己有思路的那道,不要在一棵树上吊死。我当时的策略是:先通览所有编程题,评估难度,然后把精力放在最有把握拿分的那道题上。事实证明这个策略是有效的,因为第一道题通常比第二道更容易AC。
2. C++核心考点深度拆解
2.1 内存管理:从指针到智能指针
C++方向的考题,内存管理是绝对绕不开的重点。裸指针、引用、左值右值、移动语义、RAII,这些概念几乎每次必考。我记得有一道题给了几段代码,让判断是否存在内存泄漏,选项里涉及到了异常安全的话题。如果不懂RAII,这道题很容易选错。
复习建议是手写一个简化版的unique_ptr和shared_ptr,理解引用计数的实现原理,以及为什么shared_ptr循环引用会导致内存泄漏,需要weak_ptr来打破环。这些内容不仅在笔试里高频出现,面试环节也特别爱问。
共用一个引用计数的shared_ptr在多线程环境下的线程安全性,是常考陷阱之一。shared_ptr本身的引用计数操作是原子性的,但指向的对象操作是否线程安全,要看具体业务代码,这两件事千万不要混为一谈。
2.2 聊聊C++11之后的新特性
C++17、C++20是近几年笔试的新宠。auto自动类型推导、lambda表达式、constexpr、智能指针、右值引用、完美转发、可变参数模板、结构化绑定……这些新特性在选择题里出现频率极高。特别是constexpr,很多人搞不清它从哪个C++版本引入、和const有什么区别。C++11引入了constexpr,C++14允许在constexpr函数中存放多个语句,C++17又进一步放宽了条件,C++20甚至出现了consteval和constinit。如果简历上写着熟悉C++17,结果连这些演进的差异都说不清楚,笔试和面试都会很被动。
C++20的模块、协程、概念(concept),也在大厂笔试中逐渐露脸。不要求你会写出复杂的协程代码,但至少看到这些名词时知道它是做什么用的。
2.3 STL底层原理和应用陷阱
STL的考察通常不直接问源码,而是通过具体场景来考察容器特性。比如vector的扩容机制(通常是1.5倍或2倍增长,具体由库实现决定)、list和vector的插入删除复杂度、map底层红黑树的平衡原理、unordered_map的哈希冲突处理方式、迭代器失效的几种典型情况。
真题回忆里有一道考察“vector在insert操作后,之前的引用是否依然有效”,答案是否定的,因为insert可能触发重新分配内存,而reserve可以提前规避这种问题。另一个高频考点是std::sort底层用了什么排序算法——通常是混合排序算法(introsort),数据量小时会切换到插入排序,这有效避免了纯快排在近乎有序数据上的退化问题。
2.4 设计模式与代码规范
C++方向的热词里有“c++ 设计模式”,这在笔试中经常以代码阅读题出现。单例模式(尤其是线程安全的双检锁写法)、观察者模式、工厂模式、策略模式,是出现频率最高的几个。但笔试不会让你默写某个设计模式,而是给出一段代码,问它属于哪种模式、有什么问题、怎么改进。
比如线程安全的单例,很多人知道要加锁,但忽略了“double-checked locking pattern”因为指令重排导致的隐患。正解是利用C++11的静态局部变量初始化是线程安全的,或者用std::call_once。这些细节就是选择题拉开分差的地方。
代码规范方面,笔试往往会考察一些常量的定义方式、命名规范、头文件循环依赖的规避措施。简单记忆就是:头文件尽量使用前置声明、include guard使用#pragma once(虽然它不是标准C++,但主流编译器都支持)、避免在头文件中定义非inline的全局对象。
2.5 C++小游戏与字符串处理
热词里出现“c++小游戏”还是比较有意思的,这也反映了一部分初学者的学习路径。笔试不会让你写完整游戏,但一些游戏引擎里常用的C++特性——比如对象生命周期管理、组件模式、事件分发,可能会以代码片段的形式出现。另外“c++字符串数组初始化”这类基础问题,其实是在考察数组和string在内存布局上的差异。char arr[] = {"hello"}和std::string的初始化完全不同,前者是栈上连续内存,后者涉及堆分配。这些细节即使笔试不直接出题,后续面试问C++和C兼容性时也容易涉及。
3. PHP核心考点深度拆解
3.1 PHP生命周期与运行机制
PHP方向的笔试,不会只围绕语法出题,而是更偏向工程实践。PHP生命周期(模块初始化、请求初始化、脚本执行、请求关闭、模块关闭)是理解一切PHP问题的基石。为什么PHP-FPM比mod_php更节省内存?因为PHP-FPM模式下PHP解释器常驻内存,脚本执行完只释放请求上下文,而不需要重复加载扩展。选择题会考到PHP脚本执行完成后的资源释放顺序,以及register_shutdown_function的执行时机。
热词“php错误处理”直接命中了笔试高频板块。PHP的错误级别(E_ERROR、E_WARNING、E_NOTICE、E_DEPRECATED)、异常和错误的区别、set_error_handler和set_exception_handler的使用场景,都是选择题和简答题的常客。特别注意PHP 7之后的Error类也实现了Throwable接口,所以catch (Throwable $e)能同时捕获异常和绝大多数错误,这在代码阅读题里是个关键节点。
3.2 数组底层、引用与内存回收
“php数据库pdo访问封装类”这类搜索词说明很多人刚接触PHP的数据库编程,但笔试考得更深:PHP数组的底层实现是hashtable,也就是说它同时是有序字典和数组。int和string键之间的转换规则、数组遍历时修改自身的坑、foreach和引用共用的问题,都是多选题的热门素材。比如:
foreach ($array as &$value) { // 处理逻辑 }这段代码在循环结束后,$value仍然保有一个数组最后一个元素的引用,如果后续复用$value变量,可能静默修改数组最后一个元素。这是个经典陷阱,笔试和面试都有极大几率出现。解决办法是循环结束后unset($value)。
PHP的垃圾回收机制也是考察热点,特别是循环引用情况下内存无法释放的问题。PHP 7之后引入的GC改进,以及zend_string的引用计数设计,虽然不要求你背源码,但至少要理解为什么unset不等于立即释放内存。
3.3 面向对象与设计模式
PHP的命名空间、trait、接口、抽象类、魔术方法(__construct、__destruct、__call、__get、__set、__clone、__invoke),在考题里高频出现。PHP和Java不同,一个类只能继承一个父类,但可以引入多个trait,这在代码重构场景里是很实用的机制。
设计模式在PHP侧的考察,常常结合MVC框架(比如ThinkPHP、Laravel)出现。热词“thinkphp3.2.3 { fast & simple oop php framework }”来自ThinkPHP的版本标识,老版本框架的代码在新版本PHP环境下会出现兼容性问题。笔试题可能给出一段框架代码,问你使用了哪些设计模式:容器(Container)是依赖注入的典型实现,门面(Facade)是静态代理的变体,中间件是责任链模式。不要只背模式名字,要把模式和实际代码对应起来。
3.4 Web安全:从“极客大挑战”看PHP漏洞
有趣的是,热词里出现了“[极客大挑战 2019]php”,这是一道经典的CTF题目,考察的是PHP反序列化漏洞。大厂笔试不会直接考反序列化漏洞利用,但会以代码选择题的形式考察相关安全知识,比如:
- 用户输入是否被正确过滤(SQL注入、XSS、文件上传)
- 文件包含函数(include、require)的使用是否可控
- 反序列化时是否处理了不可信数据
- 弱类型比较(==与===)的差异,特别是一个字符串跟0比较时会自动转换
PHP弱类型比较是笔试杀手题。var_dump("abc" == 0);的结果是true,因为字符串松散比较时会被转成整数0。这类细节平时写代码不一定会留意,但笔试就是专门挖这种坑。我的建议是:凡是涉及用户输入安全的代码,一律使用严格比较(===),并且用json_decode或手动类型转换做数据清洗。
3.5 数据库与PDO操作
PDO(PHP Data Objects)是PHP访问数据库的主流方式,笔试常考它的预处理机制。预处理能有效防止SQL注入,因为SQL语句结构在绑定参数之前就已经确定。选择题常见的设置是:PDO::ATTR_EMULATE_PREPARES设置为false时,才会真正使用MySQL服务端的预处理能力;如果保持默认的模拟预处理,某些情况下仍然存在注入风险。
比如这样一段代码:
$pdo = new PDO($dsn, $user, $pass, [ PDO::ATTR_EMULATE_PREPARES => false, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION ]); $stmt = $pdo->prepare("SELECT * FROM user WHERE id = ?"); $stmt->execute([$_GET['id']]);这里PDO::ATTR_ERRMODE设置为ERRMODE_EXCEPTION是很重要的,否则SQL出错时PDO默认返回false而不抛出异常,异常捕获就形同虚设了。笔试里给一段类似代码,问潜在问题或运行结果,核心就在这几个属性上。
4. Go核心考点深度拆解
4.1 goroutine与并发模型
Go方向的笔试,并发和goroutine是绝对核心,几乎每套卷子都考。goroutine的调度模型(GMP模型)、channel的收发机制、select多路复用、sync.WaitGroup、sync.Mutex和sync.RWMutex的区别,以及context的传递机制,这些是最高频的考点。
有一个经典题目是:以下代码会输出什么、是否死锁。
func main() { ch := make(chan int, 1) ch <- 1 ch <- 2 fmt.Println(<-ch) }注意make(chan int, 1)创建的是带缓冲长度为1的channel,同时发送两个数据时第二个发送会被阻塞,最终导致死锁并panic。如果题目改成make(chan int, 2),则正常输出1。这种题考察的不仅是channel基础,还包含对阻塞时机的理解。
另一个高频坑是range遍历channel时没有关闭channel导致的死锁。for range会在channel关闭后自动退出,但如果发送方忘记close,接收方会一直阻塞等待,最终造成goroutine泄漏。考试时看到for v := range ch,第一反应就是去检查是否存在close(ch)。
4.2 内存在逃逸、GC与指针
Go的指针比C++轻量,但在笔试里也有自己的考点。热词“go cgo设置msvc编译”偏向环境配置,但笔试更关心cgo的调用成本和安全限制。选择题常考:什么情况下Go的变量会发生堆逃逸(例如返回局部变量地址、闭包捕获外部变量、fmt.Println打印变量地址等),逃逸分析的决定因素是什么。
Go的GC是并发三色标记清除算法,笔试不考实现细节,但会考它和C++手动内存管理的区别。比如同样一段递归创建对象的代码,C++需要手动释放,Go则交给GC,虽然方便,但在高并发下GC的STW(Stop The World)时间会成为性能瓶颈。围绕这点出的题,通常会让你在多个优化方案中选择降低GC压力的手段,比如对象池(sync.Pool)、减少内存分配、合理设置GOGC环境变量等。
4.3 语言特性:defer、反射与错误处理
defer是Go里笔试命中率最高的语法点之一。它的执行顺序是“后进先出”,而且是函数返回前执行。如果defer语句中包含了函数调用,则该函数调用的参数在defer注册时就会被求值,但函数体直到返回前才执行。这个特性出题灵活度很高,比如:
func test() (result int) { defer func() { result++ }() return 1 }最终返回值是2,因为defer修改了命名返回值result。这个机制面试也爱问,笔试也出现过几乎一模一样的题。理解defer的执行顺位,对排查bug也会有很大帮助,尤其是涉及文件句柄关闭、锁释放等场景。
Go的错误处理(error与panic/recover)也是笔试常见内容。error是普通值传递,panic代表运行时异常,recover只能在defer函数中生效。任何recover救不了已经panic的goroutine这一点要刻在脑子里——panic之后,所在goroutine的后续代码不会继续执行,但其他goroutine不受影响。
反射(reflect)在笔试里通常以代码结果判断的形式出现。ValueOf、TypeOf、Elem、CanSet等方法的语义,以及不可导出的包私有字段不能通过反射修改这个限制,都会成为出题点。
4.4 标准库与工程化知识
Go语言笔试不会只考语法,还会涉及常用标准库的使用。热词“go 模拟键盘”属于hook类的第三方库,但笔试更多考的是io、http、json、sync、time、strconv这些官方库。有一个高频题是json.Marshal对struct字段的默认处理规则:只有导出字段才会被序列化,且如果字段没有json tag,会默认使用字段名作为key。这两个结论连在一起,就是完整的一道选择题。
还有一个高频考点是http.Server的Graceful Shutdown。在K8s环境下部署Go服务,优雅退出是必须处理的问题。考题会给出一个http server的启动代码,问怎么修改才能实现优雅关闭。正确答案通常会涉及signal.NotifyContext、server.Shutdown(ctx)等API。
4.5 Go与C++的对比题
既然笔试同时招C++、PHP、GO工程师,就难免出现跨语言对比的题目。C++和Go在内存管理上的差异、goroutine和线程的差异、编译速度的差异、部署方式的差异,都是可以出题的方向。比如goroutine的栈初始只有几KB,在线程面前是个零头,所以可以轻松创建十万甚至百万级goroutine。这类题目考察的不是具体语言语法,而是对后端工程选型的理解力,回答时多从资源开销和工程效率角度思考,方向不会偏。
5. 编程题高频题型与答题套路
5.1 ACM模式如何写主函数
笔试系统的代码输入输出通常采用ACM模式,而不是LeetCode的函数填空模式。很多人刷题习惯了核心代码模式,到了笔试现场,连读取多行输入都不会写,白白丢分。这里手动列一个标准模板:
#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n; cin >> n; vector<int> arr(n); for (int i = 0; i < n; ++i) { cin >> arr[i]; } // 业务逻辑 cout << result << endl; return 0; }注意加上ios::sync_with_stdio(false);和cin.tie(nullptr);这两行,虽然代码长了点,但能显著提升cin读取效率,尤其在输入规模达到十万及以上时,不加这两行很可能导致读写超时。如果笔试平台支持Go,主函数同样需要注意fmt.Scan的读入性能,数据量大时建议一次性读入再分割处理。
5.2 三类高频算法题型
从近两年大厂笔试看,算法题集中在三个方向:
第一,前缀和与差分。区间求和问题用前缀和可以把时间复杂度从O(n)降到O(1),差分数组则高效处理区间加减。这类题属于“会了就秒杀”的送分题,笔试一定会在某道小题或大题里出现。第二,二分答案。如果题目出现“最大化最小值”“最小化最大值”“第K大的值”等描述,大概率是二分答案,配合贪心或双指针去做check函数。第三,拓扑排序与并查集。图论题在笔试里的占比不如前两类高,但拓扑排序常出现在依赖关系或任务调度场景,并查集则常与连通分量、最小生成树组合出现。
5.3 回溯题拿部分分策略
值得注意的是,笔试算法题不会都是简单模板题,像动态规划、状态压缩、树上问题,这些题难度一上来,很多人直接就卡死了。我的建议是:如果没有AC思路,先用暴力回溯写出一个能过小规模测试用例的解,能拿部分分就尽量拿。笔试判分通常按通过的测试用例比例计算,部分得分总比零分强。
比如:
def dfs(path, remaining): if not remaining: # 处理完整路径 return for i in range(len(remaining)): dfs(path + [remaining[i]], remaining[:i] + remaining[i+1:])这类模板适合排列组合类问题,在数据量较小的情况下能奏效。若数据量大,老老实实优化剪枝条件,或者改用记忆化搜索。
5.4 编程题的输入边界与格式化
大厂笔试题通常会标注数据范围,比如n <= 10^5、数值在int范围内等。做题前先确认数据边界,否则容易因整数溢出或数组越界而反复提交失败。C++的int上限约2.1亿,当数值可能超过这个范围时,直接把变量定义为long long;Go语言则注意int在32位和64位平台下宽度不同,跨平台时使用int64更保险;PHP虽然不区分int大小写,但PHP_INT_MAX在64位平台下是远大于int范围的整数,反序列化或JSON解析大整数时要注意精度丢失。
另外,输出格式要看清楚,是多组数据还是一组数据?要求保留几位小数?最后一行是否需要换行?这些细枝末节在笔试过程中很容易被忽略,但格式错误就会导致判题不通过。
6. 备考时间线与踩坑实录
6.1 考前冲刺怎么安排
如果从现在开始准备一场两周后的笔试,我的建议是把时间分成三段:第一周补基础,重点过语言特性、操作系统、网络和数据库的选择题知识点;第二周刷算法,每天一到两题,并总结套路;考前最后两天集中做做往年真题或模拟卷,调整做题节奏。
具体到每天的时间分配,我建议早晚各一小时复习语言细节,中间的大块时间用于算法题。印象很深的是有朋友在笔试前三天还在看“c++教程”和“vscode配置c/c++环境”,这种阶段性的问题出现在考前,说明准备启动得太晚了。环境配置这类事情应该至少在笔试前半个月解决,而不是等到最后一刻。
6.2 常见环境与工具问题
笔试平台通常是网页版在线编辑器,但也有部分公司要求使用本地IDE并上传代码。如果你习惯用vscode,提前装好C/C++插件和code runner,并验证本地编译运行是否顺畅。这里有个很容易踩的坑:本地编译器版本和笔试环境的编译器版本不一致,可能导致本地的某些C++17用法在考试环境里不支持。解决办法是写代码时尽量使用C++11/14的通用语法,并提前查看笔试平台说明,确认他们使用的编译器版本。
Go环境也是一样,本地Go版本如果是1.21,笔试平台如果还是1.20,部分新API(比如min/max内建函数)可能无法编译。如果用到第三方包,笔试平台通常不允许go mod下载,所以手写代码时尽量只依赖标准库。
6.3 做题顺序与心理调节
我自己的答题顺序是:先扫描所有题目,用一分钟评估每道题的难度;先做选择题,控制在四十分钟内完成;再做最熟悉的一道编程题;最后集中攻克剩余编程题。这里的临界点是不要因为一道编程题卡壳而心态爆炸。两道编程题如果能AC一道,另一道拿部分分,就已经具备一定竞争力了。毕竟笔试是筛选性考试,不是满分考试,拿到的分数只要排在前面就是胜利。
6.4 考后复盘比成绩更重要
每次笔试结束后,我会立刻把没做出来的题思路整理下来。这个习惯让我的算法能力在一两个月内有了明显进步。笔试最忌讳的是考完就丢,下次踩同一个坑。尤其那些经典的逻辑漏洞、语言陷阱、边界条件,务必记录在错题本里,不要求抄代码,但要把错误的原因总结成一句话,方便考前快速翻看。
7. 多语言工程师的进阶学习路径
7.1 以C++为基础底座的路线
C++、PHP、Go三种语言虽然风格差异很大,但计算机底层知识是共通的。我的建议是优先把C++的底子打好,因为它能帮你理解内存、指针、编译链接、并发控制等底层概念。有了这些基础,再看Go的goroutine调度,就会发现它本质上是在线程之上封装了用户态调度;再看PHP的内存管理,也会理解它的引用计数和垃圾回收为什么是那个策略。
7.2 PHP不可被忽视的实战价值
虽然很多年轻工程师觉得PHP“不够潮”,但在Web业务快速迭代的场景下,PHP的开发效率依然极高。PHP值得精学的地方是它的Web生态环境、框架思想、以及和各种基础设施(如Redis、MySQL、Nginx)的集成套路。笔试里更多是考察PHP工程化能力,比如错误处理流程、日志规范、队列系统设计等。
7.3 Go是高性能中间件的入场券
Go在云原生方向占据着统治地位。如果目标是中间件开发、云原生基础设施、高并发网关等岗位,Go语言值得深入学习。学习路径可以从标准库的net/http开始,逐步了解rpc框架、消息队列客户端、容器编排等系统设计知识。
三种语言没有必要“三选一”学到黑。正确的策略是:选定一门主语言深入,另外两门至少要达到能读代码、能写简单项目的水平。大厂笔试既然同时招三种语言,说明他们并不指望你只精通一门,而是希望你有“多语言视角”,在处理实际问题时能选择最合适的工具。
8. 笔试之后:面试环节的语言准备
笔试通过后,紧接着是面试。面试官喜欢结合你的笔试答卷提问,比如“你第三题为什么用二分而没有用前缀和”,或者“你C++那道题为什么选择shared_ptr而不是unique_ptr”。所以笔试结束后,一定要回头复盘自己的代码,搞清楚每一行的设计理由。如果笔试时某道题是蒙的,面试前务必把这类题的解法弄透,否则面试官一追问就露馅。
C++面试高频方向包括智能指针的线程安全性、虚函数表的内存布局、STL容器优缺点、移动语义和完美转发的实际用途。PHP面试高频方向包括Composer自动加载机制、OPcache原理、慢查询日志分析、Redis在会话管理中的应用。Go面试高频方向包括GMP模型、channel底层数据结构(hchan)、sync.Pool实现细节、context的取消传播机制。准备时按这些提纲逐一梳理解答思路,用录音或写文档的方式把语言组织好。
三种语言同时考察,本质上是在逼迫你从“会写代码”升级到“理解代码为什么这样设计”。笔试只是第一道关卡,笔试里考察的那些细节,大概率会在后续面试中被再次追问。一步一个脚印走扎实,秋招的路就会越来越宽。