
我到现在还记得考百度2020校招C/PHP研发工程师笔试卷第三批那天的感受打开页面先是一愣再往下翻发现这卷子并不是那种把《王道》和《程序员面试宝典》背熟就能稳过的套路卷。它考的是C和PHP方向的底层基本功而且明显在逼你把知识“用出来”而不是“背出来”。网上关于这套题的回忆帖非常零散很多同学拿着某个版本的“真题”刷结果连题目出处都对不上。所以这篇我打算把第三批这份卷子的结构、考点、现场答题思路和备考方法一次性讲清楚不管你是准备校招还是在职跳槽都可以把它当成一份带解析的复习蓝图来用。需要先说明一点我拿不到当年试卷的官方原稿以下内容是根据同批次考生的口述回忆、百度历年校招题库规律以及我自己秋招时的实际答题记录整理还原的。题目颗粒度不等于逐字原文但知识点覆盖和难度曲线基本可以对得上。你把这篇文章当成“基于真实考场记忆的深度复盘”来看含金量比单纯背题更高。1. 试卷结构拆解为什么C和PHP会被放进同一张卷子1.1 第三批在百度校招流程里的实际位置百度校招的笔试是按批次滚动放的第三批通常在九月下旬到十月上旬。很多人以为批次越靠后题目越难其实不是。第三批和第一批、第二批在难度上基本持平区别是知识点抽签不一样比如第一批多考了图论第二批偏重网络第三批则把大量分值压在了语言机制和工程场景上。换句话说你准备的广度决定了你能不能稳定发挥而不是赌某一道题会不会出。从流程看这一批笔试通过后会直接进入面试环节笔试成绩会成为面试官手里第一份“技术画像”。所以这份卷子不光是筛人也是给面试官划重点你在卷子里暴露出来的弱项基本就是面试时会被追问的深水区。我当时就在快速幂那道题上踩了取模不及时的坑结果一面二面都被追问了不下三遍。1.2 C岗与PHP岗在同一张卷里的不同侧重这张卷子的特别之处在于C和PHP研发工程师共用一套笔试框架但不同方向的题有不同侧重点。你去看岗位描述也会发现百度对C工程师的要求偏底层系统和搜索引擎核心模块对PHP工程师的要求则更贴近Web服务、业务接口和数据存储。把这层逻辑理清楚复习方向就明确了。我整理了一张对比表方便你对照自己报考的方向去分配精力考察维度C方向侧重点PHP方向侧重点语言特性指针、内存布局、虚函数、移动语义数组与函数、魔术方法、命名空间、错误异常数据结构与算法排序、字符串、快速幂、手写STL容器排序、队列、字符串处理数据库索引B树原理、事务隔离级别PDO封装、SQL注入防御、慢查询优化网络TCP/UDP、滑动窗口、TIME_WAITHTTP协议、跨域、会话管理工程场景内存池、线程池、无锁队列单例模式、消息队列、接口幂等注意一点无论报哪个方向公共部分数据结构、算法、网络、操作系统都是必考的所以千万别只盯着语言特性刷题。第三批的考卷里公共基础部分大约占四成语言方向题占四成剩余两成是开放场景题。2. C方向的硬核考点从语法细节到内存模型2.1 选择题里的高频陷阱const、指针、虚函数与内存布局C方向的选择题给我最大的感觉就是“每个选项都像对的但只有一个能经得起推敲”。我挑三个考得最密集的知识点展开你在复习时也可以按这个优先级来。第一个是const。考卷里出现了“顶层const与底层const的区别”这一经典题目具体问法是类似“const int* p、int* const p和const int* const p分别代表什么”。很多人只记了“const在星号左边修饰指向的内容在右边修饰指针本身”但这不够。真正需要理解的是顶层const表示指针本身不可修改底层const表示指向的对象不可通过该指针修改。还有一个容易忽略的细节是数组与const结合时比如const char* arr[]和char* const arr[]语义完全不同前者是字符串常量数组后者是指针数组且指针本身不能变。这类题一旦考到函数传参就会引出一个更深的考点void func(const char* p)能接收什么类型的参数、不能接收什么涉及字符串字面量的类型退化。第二个是虚函数与内存布局。我记得有一道题给了四个选项构造函数能不能是虚函数、析构函数要不要声明为虚函数、静态成员函数能不能是虚函数、虚函数是编译期绑定还是运行期绑定。正确答案是建议把基类析构函数声明为虚函数其他三个都是错的。构造函数不能是虚函数因为对象在构造完成前虚表指针还没建立无法完成动态绑定静态成员函数没有this指针也没法走虚表虚函数在运行期通过虚表解析所以普通调用会带来一次间接跳转的开销。这里面试官真正想看的是你知不知道虚表指针存放在对象起始位置、虚表由每个类各维护一张、派生类覆盖虚函数后虚表里对应槽位会被替换。第三个是内存管理。C方向的题很自然地会引到RAII和智能指针上。unique_ptr为什么不能拷贝只能移动因为它独享所有权拷贝会导致同一块内存被释放两次而shared_ptr的引用计数是线程安全的但被指向的对象并不自动线程安全这两件事要分清。还有weak_ptr的作用是解决循环引用它不会增加引用计数使用时需要调用lock()提升为shared_ptr如果对象已释放lock返回空指针。2.2 手写编程题字符串处理、排序和快速幂的现场正确写法编程题是C方向的重头戏第三批让我印象最深的有三道实现一个String类、手写冒泡排序或选择排序、用快速幂求a的b次方模p。这三道题难度都不高但很能暴露基本功。先看String类的考点。核心是深拷贝与浅拷贝#include cstring class String { public: String(const char* s nullptr) : data_(nullptr), len_(0) { if (s ! nullptr) { len_ strlen(s); data_ new char[len_ 1]; strcpy(data_, s); } else { data_ new char[1]; data_[0] \0; } } String(const String other) : data_(nullptr), len_(other.len_) { data_ new char[len_ 1]; strcpy(data_, other.data_); } String operator(const String other) { if (this ! other) { char* tmp new char[other.len_ 1]; strcpy(tmp, other.data_); delete[] data_; data_ tmp; len_ other.len_; } return *this; } ~String() { delete[] data_; data_ nullptr; } private: char* data_; size_t len_; };这道题有几个隐藏扣分点构造函数必须处理空指针和空字符串的差异拷贝赋值运算符必须处理自赋值先new新空间再释放旧空间避免内存分配失败时把原对象搞坏析构后最好把data_置空。如果你只是写一个默认浅拷贝的类编译能过但运行必崩。这个题考察的不只是语法而是你是不是真的理解内存分配和释放的生命周期。然后是排序。考卷里常见的是让写冒泡排序或选择排序并要求分析稳定性。这里我给一个冒泡的优化版本void bubbleSort(int arr[], int n) { for (int i 0; i n - 1; i) { bool swapped false; for (int j 0; j n - 1 - i; j) { if (arr[j] arr[j 1]) { std::swap(arr[j], arr[j 1]); swapped true; } } if (!swapped) { break; } } }加一个swapped标志位是很多人会漏的细节因为当序列已经有序时不加标志位依然会跑完整的O(n²)循环。选择排序不稳定这一点也是考点原因是选择排序会把后面的元素直接换到前面可能破坏相等元素的相对顺序。如果题目继续追问“插入排序为什么稳定”“快排为什么不稳定”你要能从“交换是否跨过相等元素”这个角度去解释。最后是快速幂。这道题出现的频率非常高不仅百度考很多大厂都考因为它能同时考察位运算、分治思想和取模优化。现场写法如下long long fastPow(long long a, long long b, long long p) { long long res 1 % p; a % p; while (b 0) { if (b 1) { res res * a % p; } a a * a % p; b 1; } return res; }这里的细节包括如果p可能为1res的初始值不能写成1否则结果恒为1a要先取模否则第一次乘就可能溢出中间乘法用long long承接因为两个int相乘的结果可能超过int范围。时间复杂度是O(log b)相比循环乘b次是质的提升。我当时就是忘了初始res那块导致p等于1的时候答案全错这属于典型的边界条件意识不足。2.3 冷门但拉分的设计模式与语言新特性C方向的选择题里还会出现少量设计模式和语言新特性比如单例模式的线程安全写法、constexpr是哪个C版本引入的、回调函数的使用场景。constexpr是C11引入的这个偏记忆但常考它让表达式可以在编译期求值C14放宽了函数体限制C20又加了consteval和constinit。理解了这条演进线选择题怎么变都不慌。回调函数在C里通常用函数指针、std::function和lambda表达。考卷里可能给一段代码问输出顺序实际是在考察函数指针的类型匹配。设计模式更倾向于在开放题里出现比如让你设计一个日志系统你会怎么用单例模式避免重复初始化、用观察者模式解耦日志输出目标。这里我建议你把单例、工厂、策略、观察者这四种模式各自写一遍C实现面试笔试都够用了。3. PHP方向的实践考点从语言特性到Web工程能力3.1 函数细节与面向对象substr边界、运算符优先级、魔术方法如果你报的是PHP方向这份卷子对PHP语言本身的考察非常细致绝不是背几个函数名就能过。第一道常见选择题是substr的边界行为比如substr(hello, -3)返回什么、substr(hello, 2, 10)越界时返回什么。前者返回llo因为负数偏移量从字符串末尾开始算后者返回llo因为当长度参数超出可用范围时会截到字符串末尾但不会报错。这些细节如果你平时只用框架的helper函数可能真的一时想不起来。运算符优先级是另一个高频考点。PHP里.和混用、??和?:的区别都属于“看起来简单但一做就错”的题。比如$a 1 2px在PHP 7里的结果是什么在PHP 7及以后字符串开头如果是数字就取数字部分参与运算结果是3但如果你写成2px 1结果也是3。真正要小心的是PHP 8.0之后字符串与数字的比较规则变了涉及数字字符串和非数字字符串之间的比较结果可能不同。这些细节推荐你把PHP手册的“类型比较表”重新过一遍比刷十道题管用。面向对象方面喜欢考魔术方法。__construct、__destruct、__get、__set、__isset、__unset、__call、__callStatic、__toString、__clone这些方法的触发时机你得烂熟于心。比如访问一个不存在或不可访问的成员变量时触发__get调用一个不存在或不可访问的方法时触发__call用clone复制对象时默认浅拷贝如果需要深拷贝必须在__clone里手动复制引用类型的成员。实际题目可能会给你一段代码问最终的输出顺序这就要把构造、方法调用、析构的先后全部理清楚。3.2 数据库访问与接口设计PDO封装、跨域JSONP、错误处理PHP方向的编程题更贴近业务我记得有一道是让写一个PDO数据库访问封装类并要求实现单例模式。这类题的考察点有两层第一层是会不会用PDO第二层是能不能把工程规范落到代码里。一个合格的封装至少要支持以下能力class Db { private static $instance null; private $pdo; private function __construct(array $config) { $dsn sprintf(mysql:host%s;dbname%s;charsetutf8, $config[host], $config[dbname]); $this-pdo new PDO($dsn, $config[user], $config[pass], [ PDO::ATTR_ERRMODE PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES false, ]); } public static function getInstance(array $config) { if (self::$instance null) { self::$instance new self($config); } return self::$instance; } public function query($sql, array $params []) { $stmt $this-pdo-prepare($sql); $stmt-execute($params); return $stmt; } public function fetchAll($sql, array $params []) { return $this-query($sql, $params)-fetchAll(); } }这段代码里有三个工程细节值得展开。第一构造函数是private的这是单例模式的标志外部不能直接new。第二PDO构造函数配置里开启了异常模式SQL出错会抛PDOException而不是静默返回false这对调试和日志记录都有帮助。第三ATTR_EMULATE_PREPARES设置为false表示使用真正的预处理语句也就是把SQL模板和参数分开发送给MySQL服务端。这样做的主要收益是避免拼接SQL带来的注入风险。关于PHP跨域和JSONP这也是Web方向的高频题。很多人知道CORS是主流方案但考卷里可能要求你写一个JSONP的返回格式。JSONP的核心是利用script标签不受同源策略限制这一点让服务端动态返回一段JavaScript调用header(Content-Type: application/javascript; charsetutf-8); $callback $_GET[callback] ?? callback; $data json_encode([ code 0, message ok, data [user admin] ], JSON_UNESCAPED_UNICODE); echo $callback . ( . $data . );这里要注意callback参数必须做白名单校验否则会形成反射型XSSJSONP只支持GET请求没法处理复杂业务想支持POST、PUT和自定义头还是得靠CORS。CORS的真实场景里非简单请求会先触发一次OPTIONS预检请求服务端必须返回正确的Access-Control-Allow-Origin、Access-Control-Allow-Methods和Access-Control-Allow-Headers否则浏览器会拦截。这道题如果你能主动把JSONP和CORS的取舍讲清楚面试官会对你有很好的印象。PHP错误处理也是第三批笔试里的常客。PHP 7之后Throwable接口成为所有异常和错误的基类TypeError、ParseError、Error都实现了Throwable。实际业务里正确的做法是用set_error_handler处理PHP传统警告用set_exception_handler做兜底关键业务再单独用try/catch包一层。生产环境必须把display_errors关闭把log_errors打开避免把堆栈信息直接暴露给用户。3.3 从热词看趋势序列化、队列与框架源码除了基础考点这批试卷里还渗透了不少“工程素养”题其中最典型的是PHP序列化和反序列化以及PHP在消息队列里的应用。序列化中文乱码问题、serialize和json_encode的取舍都是常考内容。serialize会把中文转成带有长度前缀的编码表示json_encode配合JSON_UNESCAPED_UNICODE会保留中文字符。从内存占用看json格式通常比serialize格式更紧凑但serialize能保留PHP对象的类型信息因此框架缓存Session时常常两者混合使用。队列在PHP面试里出现得越来越多因为Web服务天然需要处理异步任务。考卷里可能会问“不使用Redis只靠MySQL怎么实现一个简单队列”。合理答案是建一张task表字段包括id、task_type、payload、status、created_at每次取出一条status为pending的记录并更新为processing处理完成后再更新为done。这里要考虑的是并发取任务时的原子性可以通过UPDATE ... WHERE statuspending LIMIT 1配合受影响行数来判断或者使用SELECT ... FOR UPDATE。框架源码层面热词里出现的ThinkPHP 3.2.3是年代比较久远的框架但它的MVC结构很清晰入口文件、路由解析、控制器、模型、视图。笔试不一定会深入考框架源码但面试官常会问“你读过哪个框架的源码”。哪怕你只用过Laravel或ThinkPHP也建议把请求生命周期梳理一遍从public/index.php入口到路由分发、中间件、控制器再到响应返回这个流程本身就是一套完整的Web架构知识。4. 计算机基础与开放题拉分题背后的隐藏逻辑4.1 操作系统与网络进程线程、三次握手、TIME_WAIT公共基础部分考得最稳定的两个方向是操作系统和网络。进程与线程的区别几乎是必考进程是资源分配的最小单位线程是CPU调度的最小单位同一进程下的线程共享地址空间但进程之间地址空间相互隔离。进一步追问往往是“多线程和多进程怎么选”这就要结合上下文切换开销、数据共享复杂度、崩溃隔离性来回答。网络方面TCP三次握手不仅考过程还考为什么是三次而不是两次或四次。原因是需要确认双方都有收发能力并且要同步初始序列号。TIME_WAIT也常考主动关闭连接的一方在收到对方的FIN后会进入TIME_WAIT持续2MSL报文最大生存时间的两倍目的是保证最后一个ACK能到达对方同时让旧的报文段在网络中自然消失避免干扰新连接。如果你自己做过网络编程肯定知道高并发短连接场景下TIME_WAIT过多会占用端口解决办法是开启tcp_tw_reuse或使用长连接但考试只要你把原因说清楚就够了。HTTP和HTTPS的区别、GET与POST区别、Cookie与Session区别属于送分题但一定要答得精准。比如GET语义是获取资源参数放在URL里POST语义是提交数据参数放在请求体里。更深的考点是HTTP是无状态协议Session靠服务端保存数据Cookie保存SessionID如果跨域场景下还需要处理Cookie携带策略SameSite属性。4.2 开放场景题短链接服务、秒杀系统里的架构意识开放题是第三批整张卷子最有区分度的部分也是很多人最不重视的部分。我遇到的两道开放题一道是设计短链接服务一道是设计秒杀场景下的库存扣减。短链接服务的核心思路是把长URL映射成一个短码然后重定向到原始地址。可以用的方案是发号器生成全局自增ID再把ID转成62进制字符串对应线上短码存储层用数据库记录id、short_code、original_url、expire_time缓存层用Redis加速读取。重定向时用302还是301这个细节很关键301是永久重定向浏览器会缓存后续访问不再请求服务端302是临时重定向每次都要经过服务端可以记录访问统计。做短链接通常选302既能统计点击量也方便随时禁用链接。秒杀系统的库存扣减最容易想到的方案是直接操作数据库UPDATE goods SET stock stock - 1 WHERE id ? AND stock 0这个SQL自己本身就带条件能防超卖。但高并发下数据库会成瓶颈所以常规思路是前置Redis派发令牌先用Redis的DECR操作扣减库存扣减成功才允许请求真正落库。这里必须注意Redis中库存和数据库中库存的一致性需要做异步对账不能简单依赖过期时间。开放题不要求你给出唯一答案而是看你能不能把数据一致性、并发控制、缓存穿透这些点都覆盖到。5. 复盘与备考路线按考纲倒推你的复习计划5.1 我个人实测的踩坑清单现在进入最有价值的复盘环节。我把当时在第三批卷子里踩过的坑以及后来帮学弟学妹模拟时反复出现的扣分点统一列成清单你复习时直接对照快速幂忘了处理取模导致溢出或者res初始值没考虑p1的边界。这个属于“代码看起来对实例一跑就错”的典型。String类只写了构造函数和析构函数忘了拷贝构造和赋值运算符或者拷贝赋值时没有处理自赋值。很多非科班同学在这道题上直接暴露底子。排序算法只背了模板不知道冒泡优化可以用swapped提前退出也不知道选择排序为什么不稳定。PHP的PDO封装没有把ATTR_EMULATE_PREPARES设置为false面试官一眼就能看出你没有真正理解预处理与参数绑定的价值。JSONP的callback参数没有做白名单校验尤其在安全题里会被扣分。网络题里答得出三次握手过程却答不出为什么不是四次。面试官稍微追问就露馅。开放题只写结论不写理由比如“应该用Redis做缓存”但没有说明缓存什么数据、怎么保证一致性、缓存穿透了怎么办。自查一下如果你现在能把这些坑都填上这份卷子基本就稳了。5.2 倒推式复习计划从T-4个月到T-1周备考校招笔试最忌讳“小红书式刷题”——看起来刷了很多实际上没有体系。我建议把时间线拉长到四个月按阶段推进。T-4个月到T-2个月主攻语言基础和算法基础。C方向把《C Primer》前五章和标准库常用容器过一遍配合LeetCode上的数组、链表、字符串、排序、二分、动态规划分类刷题。PHP方向重点过PHP手册的函数参考、面向对象章节以及PDO的官方文档。这一阶段的目标不是刷难题而是确保基础题和中等题都能快速AC同时把语言特性理解到能给别人讲明白的程度。T-2个月到T-1个月进入专项突破。用真题和模拟卷做题型归纳把高频考点按表格整理C的虚函数与内存布局、移动语义PHP的魔术方法、错误处理、文件上传公共部分的网络和操作系统。每归纳一个知识点就配套练3到5道对应题目而不是只停留在“看过”。我自己的习惯是把错题整理成Markdown笔记每个错题附上错因、正确思路和同类题链接后期复习效率非常高。T-1个月到T-2周开始刷成套的模拟卷并严格限时。百度这类笔试的时间一般在一个半小时到两个小时选择题加编程题编程题必须留出至少40分钟。模拟时一定要在真实OJ环境下敲代码别只在IDE里跑通就完事因为笔试系统的输入输出格式处理本身就是隐性考点。T-2周到T-1周回归错题和基础概念不再追求难题。把之前整理的错题笔记过一遍把算法模板快速幂、排序、二分、并查集、拓扑排序全部手写一遍保证肌肉记忆。这时候如果再发现某个知识点还不熟果断放弃深挖把时间留给高频考点和稳定得分点性价比更高。关于本地刷题环境C方向建议用VSCode加MinGW-w64或Visual C Build Tools配好tasks.json和launch.json之后就可以在本地单步调试。PHP方向建议装一个PHP CLI环境命令行直接跑php文件用netbeans或VSCode都行但不需要为了刷题去配完整的Nginx和PHP-FPM。环境配置这件事越简单越好别把时间耗在折腾工具上。还有一点做题时间分配要提前定好。选择题如果卡壳超过两分钟先凭第一直觉选一个并标记编程题则按“读题-列测试用例-写框架-补细节”的顺序来。我在考场上的习惯是先跑一遍题目给出的示例再跑两个自己构造的边界用例比如空字符串、最大值、负数输入这样能捡回很多本来会丢的分。整理完这份卷子的考点和踩坑记录我自己最大的体会是校招笔试考的不是你有多聪明而是你在有限时间里把基础概念转化成可运行代码的熟练度。这份熟练度没有捷径就是靠按体系刷题、按场景复盘堆积出来的。你如果能把这篇里提到的CString类、快速幂、PHP的PDO封装、跨域方案这些点逐一手写清楚第三批同级别的卷子基本不会成为你秋招路上的拦路虎。