☰
360校招Windows开发笔试:C++内存、线程同步与安全机制考点拆解
2026/10/17 14:33:13 网站建设 项目流程

准备360校招笔试的时候,我把Windows开发工程师岗位的客观题翻来覆去研究了很多遍。2019年这套题在不少求职群里被称为"一套能劝退小白的合集"——倒不是计算量大到离谱,而是它考察的全是平时写业务代码碰不到、但操作系统和编译器一直在默默工作的那一层东西:C++内存布局、进程线程同步、消息队列、DLL加载、系统注册表,甚至还掺杂了缓冲区溢出这类安全原理题。这篇文章不打算贴整套题了事,而是把题目背后真正要考的每个知识板块拆开,配上典型的客观题变体和解题思路,给准备校招的同学一个可以照着复习的路线图。

1. 笔试整体设计与考点分析

1.1 360校招笔试题的风格特点

先说一下这套笔试客观题的整体结构。单选、多选和判断题混排,覆盖面横跨计算机基础、C/C++语言、操作系统、计算机网络和Windows平台机制几个大块。相比一般互联网公司的通用笔试题,360的客观题有两个很明显的差异。

第一,安全题是"隐藏主线"。360做安全起家,笔试里很自然会出现缓冲区溢出、整数溢出、提权、DLL劫持这类题目。就算不是网络安全岗位,Windows开发工程师也要面对其中一部分,毕竟Windows开发本身就绕不开这些底层机制。你不会这些,别说笔试,后面面试聊起项目里的崩溃分析、漏洞修复,一样会被问住。

第二,Windows平台相关知识的比例明显偏高。通用校招可能只考一两道"进程间通信方式有哪些",但这份合集里,静态链接和动态链接的区别、DLL搜索顺序、注册表自启动位置、消息循环中GetMessage和PeekMessage的差异,这些Windows开发专属知识点会被反复考到。也就是说,平时只刷LeetCode、只背计算机网络八股文的同学,遇到这套题会明显感觉到"知识盲区"。

1.2 Windows开发工程师岗位需要什么能力

从笔试题目倒推岗位要求,其实能画出一张很清晰的能力画像。360要的Windows开发工程师,不是只会拖控件写界面的人,而是能在系统层面思考问题的人。

第一层是C/C++语言功底。指针、内存分配、字符串处理、STL容器,笔试里通过选择题考察你对底层细节的理解深度。比如sizeof在数组和指针上的差别,new和malloc的差异,结构体内存对齐的计算规则。这些不是背一背就能过的东西,必须真正动手写过、调试过。

第二层是操作系统原理。进程和线程的区别、线程同步的几种手段、进程间通信的适用场景、虚拟内存和物理内存的关系,这些内容在《操作系统》课本里都有,但笔试会用具体场景来考。它不直接问你"什么是死锁",而是给你一个多线程并发访问资源的代码场景,问你哪个选择能避免死锁。

第三层是Windows平台机制。消息机制、窗口过程、DLL、注册表、句柄和内核对象,这部分是普通计算机基础课程覆盖不到的地方,需要专门看《Windows核心编程》这类资料。很多科班出身的同学在这里翻车,因为没有系统教材讲过这些东西。

第四层是安全思维。不是说非得做漏洞挖掘,而是你得知道一个不安全的代码长什么样。缓冲区溢出为什么危险?为什么不要在DllMain里做复杂操作?UAC提权之后的令牌有什么不同?这些概念都会在笔试题里出现。

备考时我的建议是:别抱着一本面经死记硬背,而是按这四层能力去复习,每一层都找一两本经典书或官方文档吃透。底层知识扎实了,客观题就是换个姿势考你早已掌握的东西。

2. 核心知识点拆解:Windows开发的地基

这套题考来考去,其实就是在几个固定领域里反复出题。我把它们拆成六块,每一块对应高频考点,复习时逐项过一遍就能覆盖大多数题目。

2.1 C/C++基础与内存管理:笔试最容易失分

先说C/C++,这是客观题里占比最大的一块。很多同学觉得C++语法都懂,结果一做到细节题就露馅。

第一类是sizeof题目。面试官很喜欢把数组、指针、结构体混在一起考。比如:

void func(char arr[100]) { int size = sizeof(arr); }

这里sizeof(arr)是多少?在x64平台下是8,因为数组作为函数参数会退化成指针。这个知识点是选择题里的常客。很多人觉得char arr[100]传入函数后就还是100,实际不是,它已经变成了指针。类似的变形还有:sizeof("hello")是6(包含末尾的'\0'),strlen("hello")是5,这两个结果摆在一起就能看出数组和字符串的区别。

第二类是内存对齐计算。给一个结构体,让你算它的sizeof,这题几乎是每套笔试题的标配。规则其实只有三条:每个成员的偏移量必须是自身对齐数的整数倍;结构体总大小必须是最大对齐数的整数倍;编译器默认对齐数可以通过#pragma pack修改。知道这三条,绝大多数结构体大小题都能解出来,不用背任何特例。

第三类是堆和栈的区别。栈上变量由编译器自动管理,分配速度快,但有大小限制;堆上内存需要手动申请和释放,速度慢但容量大、能在函数间共享。笔试里经常考"哪些对象在栈上,哪些在堆上",以及new一个对象之后没delete会怎样。这里有个容易忽略的细节:new[]配delete[],new配delete,混用就是未定义行为。

第四类是常见内存错误。野指针、悬垂指针、重复释放、内存泄漏,每种都有对应的选择题场景。比如:

int* p = new int(10); delete p; delete p; // 会发生什么?

这道题考的是重复释放可能导致未定义行为,轻则崩溃,重则被利用。真正在现场排查两天才能定位的崩溃,在笔试里只是一道选择题,但也说明这些知识点不是纸上谈兵。

2.2 进程与线程:多任务机制的理解

进程是资源分配的基本单位,线程是调度执行的基本单位。这句话背得很熟,但笔试考的是理解。

进程和线程的主要区别:进程有独立地址空间,一个进程崩溃通常不会直接拖垮另一个进程;线程共享所在进程的地址空间,一个线程踩内存可能让整个进程崩溃。切换开销上,线程比进程小得多,因为线程切换不需要切换地址空间。这两个结论基本能回答大多数"概念判断"题。

线程同步是这一节的绝对核心。Windows下常用的同步原语有四个:临界区、互斥量、信号量、事件。笔试特别喜欢考它们之间的区别:

  • 临界区是用户态对象,速度快,但只能用于同一进程内的线程同步;
  • 互斥量是内核对象,可以跨进程使用,能设置等待超时;
  • 信号量维护一个计数器,用于控制并发访问资源的线程数量;
  • 事件适合做线程间的通知,比如"主线程通知工作线程可以开始干活了"。

这些选择题经常给一个场景,问"多个进程需要互斥访问同一个共享文件,应该选哪个"。答案基本是互斥量,因为临界区跨不了进程。如果题干强调"同一进程内的多个线程"且"开销最小",那答案往往是临界区。

进程间通信也是高频考点。Windows下常见的IPC方式有管道、共享内存、消息队列、Socket。管道适合简单的一对一数据流,共享内存适合大数据量的高频交互,Socket则是最通用、能跨主机的方案。做题时抓两个关键词:数据量大小和是否跨主机,基本就能锁定答案。

2.3 窗口编程、消息机制与字符编码

Windows窗口程序的核心是消息循环。程序启动后,系统给你一个消息队列,GetMessage从队列里取消息,DispatchMessage把消息分发给对应的窗口过程,窗口过程处理完再回到循环取下一条,这就是"消息驱动"的基本流程。这个概念搞懂之后,很多选择题就迎刃而解。

GetMessage和PeekMessage的区别是高频考题。GetMessage是阻塞的,队列里没消息就卡在那里等;PeekMessage是不阻塞的,队列没消息立刻返回FALSE,所以它常用于游戏主循环这类需要持续刷新的场景,在循环里先PeekMessage处理完窗口消息,再跑一帧游戏逻辑。

消息类型里,WM_PAINT相关的题也很常见。窗口需要重绘时,系统会生成WM_PAINT消息,但它是低优先级消息,只有在队列里没有其他消息时才会被处理。这就是为什么窗口拖动时会"卡顿"或出现重绘不及时的视觉现象。InvalidateRect用于主动把某个区域标记为无效,强迫系统下一次空闲时发送WM_PAINT。这里有一个易错点:InvalidateRect只是"标记无效",并不是"立刻重绘",真正绘制要等WM_PAINT被取出。

再说字符编码。很多Windows开发新人遇到的第一道坎就是程序里中文乱码,这个场景在笔试里也多次出现。Windows内部传统上用ANSI编码(在简体中文系统下即GBK/GB2312),后来微软推荐改用Unicode,也就是UTF-16的wchar_t。而现代跨平台程序普遍使用UTF-8。为什么乱码?本质上是编码和解码不一致:同一个字节序列,用GBK解读和用UTF-8解读出来的字符完全不同。用MultiByteToWideChar和WideCharToMultiByte做转换时,如果代码页参数传错,就会出现乱码。刷到"乱码"相关的题时,核心思路就一条:先确认源编码是什么,再确认目标编码是什么,第三步确认转换API的代码页参数是否匹配。

2.4 DLL机制与动态链接

DLL在Windows开发里的地位,就像.so在Linux里一样重要。笔试里关于DLL的题主要集中在三个方向。

静态库和动态库的区别。静态库的代码在链接时被打进exe,程序体积大,但运行时不需要额外文件;动态库的代码在运行时才被加载,exe体积小,多个进程可以共享同一份DLL代码,更新DLL不需要重新编译exe(前提是接口不变)。选择题里如果问"如何在不重新编译主程序的前提下修复一个bug",答案方向就是动态库。

隐式链接和显式加载。隐式链接就是在编译时通过.lib导入库声明了DLL里的函数,程序启动时由加载器自动加载DLL;显式加载则是调用LoadLibrary在运行时按需加载,再通过GetProcAddress拿到函数指针。笔试经常问两种方式的适用场景:不想让程序一启动就加载某个DLL,或者想在系统路径下按顺序查找库,就用显式加载。

DllMain的陷阱。在DllMain里做复杂操作是大忌,因为当进程创建线程或加载其他DLL时,系统会持有一个加载锁,在DllMain里调用LoadLibrary、创建线程、等待线程结束等操作,都可能造成死锁。笔试题经常给出一段在DllMain里干活的代码,问你"哪个操作可能导致死锁",答案就是那些会触发加载锁的调用。

2.5 注册表与系统配置

注册表是Windows的配置数据库,说白了就是一个分层的键值对存储。笔试里关于注册表的考题,通常是让你判断某些信息存在哪个根键下,或者某个自启动配置写在哪里。

注册表五大根键里,最常考的是HKEY_LOCAL_MACHINE(HKLM)和HKEY_CURRENT_USER(HKCU)。HKLM存放的是系统级配置,所有用户共享,修改需要管理员权限;HKCU存放的是当前用户的配置,不需要管理员权限。软件的自启动项通常会写在HKCU\Software\Microsoft\Windows\CurrentVersion\Run或HKLM\Software\Microsoft\Windows\CurrentVersion\Run下。运行regedit能看到整个注册表结构,命令行下用reg query、reg add可以做查询和修改。

还有一个常考的细节:32位和64位程序读写注册表时,存在注册表重定向。64位Windows上,32位程序访问HKLM\Software时会被重定向到HKLM\Software\WOW6432Node,所以同一个路径下32位和64位程序读到的东西可能不一样。这个点很容易考,也很容易在实际开发中踩坑。如果笔试里出现"为什么我用regedit能看到这个键,但程序里读不到"的题,多半是在考这个重定向机制。

2.6 安全基础:360笔试的特色题目

最后这节是360的"特色菜":安全基础。虽然岗位是Windows开发工程师,但笔试里会出现一定比例的安全相关题,而且通常会和系统机制结合。

最经典的是缓冲区溢出。核心原理很简单:局部变量在栈上连续布局,比如:

void vuln(char* input) { char buf[8]; strcpy(buf, input); }

strcpy不会检查目标缓冲区大小,如果输入超过8字节,数据就会越过buf的边界,一路向上覆盖栈上保存的返回地址。攻击者精心构造输入数据,就能让函数返回时跳到攻击者控制的地址执行代码。笔试里常见的问法是把溢出后的执行流走向问清楚,或者让你判断哪些函数不安全。strcpy、gets、sprintf都是高危函数,正确选项是带长度限制的strncpy、fgets、snprintf。

除了缓冲区溢出,整数溢出也不少见。一个int变量加到溢出变成负数,如果这个值又被用来计算缓冲区大小或者循环次数,就可能引发安全问题。格式化字符串漏洞也是高频科普题:printf(user_input)直接把用户输入当格式字符串用,攻击者用%x、%n就能读取或改写内存。这类题的解题思路很直接,就是看代码里是否有"用户输入直接进入格式化字符串"或"用户输入直接进入无长度限制的拷贝函数"。

DLL劫持这个概念也很值得复习。Windows加载DLL时按一定顺序搜索目录,可以简化为:已知DLL优先,然后是应用程序所在目录、系统目录、Windows目录、当前目录,最后是PATH环境变量中的目录。如果某个目录可写而搜索路径里又没有明确指定DLL,攻击者就可以放一个同名恶意DLL,让程序加载它。笔试里出现过让你判断"哪个目录优先级最高"的题,也出现过"如何防止DLL劫持"的题。对这个话题,安全公司向来非常敏感,做Windows开发迟早会碰上。

3. 客观题实战解析:从题目看考点

前面把知识点过了一遍,接下来直接上几道典型的客观题变体,说说做题时的思路。这些题都是这套考点的高频变形,思路相通,可以用来练手。

3.1 C++与内存考点:从一道sizeof题说起

第一题,判断sizeof的输出结果:

struct Node { char flag; int* ptr; char name[9]; };

在64位系统、默认对齐条件下,sizeof(Node)是多少?

解题思路:先看对齐数。flag占1字节;ptr是8字节指针,偏移量需要对齐到8,所以flag后面要补7个填充字节,ptr落在偏移8的位置;name是9字节,char的对齐数是1,直接跟在16字节之后;结构体总大小需要是最大对齐数(8)的整数倍,当前是25,因此对齐到32。答案是32。

这道题的坑在于:很多人只算字段总长度(1+8+9=18),忽略了填充字节和对齐规则。做这类题,记住三条对齐规则就够用,不用背任何编译器特例。如果题目加了#pragma pack(1),那所有对齐都取1,总大小就会变成18,这种反差也是常考的变形。

第二题,判断下面代码的输出:

char str[] = "hello"; char* p = str; cout << sizeof(str) << endl; // ? cout << sizeof(p) << endl; // ?

sizeof(str)是6,因为数组包含结尾的'\0';sizeof(p)在64位系统上是8,因为p是指针,跟它指向的内容长度无关。这两个结果放在一起对比,就能看出数组和指针的本质区别。如果题目再延伸问strlen(p),那就是5,因为strlen数到'\0'就停了,不计入结尾符。

3.2 进程线程考点:同步与通信怎么选

第三题,场景题:三个线程都需要向同一个全局链表写入数据,而且不允许一个线程写一半被另一个线程打断,要求同步开销尽量小。以下哪个方案最合适?

A. 临界区 B. 互斥量 C. 事件 D. 信号量

正确答案是A。因为所有线程都在同一进程内,临界区是用户态同步原语,不需要进入内核,开销最小。互斥量虽然也能用,但它是内核对象,跨用户态和内核态的开销更大。这个场景里如果选互斥量,不一定错,但"开销尽量小"这个条件就暴露了考点。事件和信号量在这个场景下不是最合适的同步手段。

第四题,场景题:进程A需要持续向进程B发送大块数据,两个进程都已经在本机运行,想要最高效的通信方式,应该选哪个?

A. 命名管道 B. 共享内存 C. 本地Socket D. 发送WM_COPYDATA消息

正确答案是B。共享内存是Windows本地进程间通信里带宽最高、延迟最低的方式,适合大块数据交互。命名管道适合流式数据,Socket跨主机通用但单次传输效率不如共享内存。WM_COPYDATA可以传少量数据,但不适合大数据量高频交互。这类题的核心是记住各种通信方式的定位:管道偏简单流,共享内存偏大数据,消息偏零散控制信号。

3.3 Windows机制与安全考点:经典题目的陷阱

第五题,关于WM_PAINT,下列说法正确的是?

A.WM_PAINT消息和其他消息一样排在普通队列里,先到先处理 B. 调用InvalidateRect后,WM_PAINT会立刻被发送 C.WM_PAINT是低优先级消息,队列里没有其他消息时才会被取出 D. 只有用户点击窗口时才会产生WM_PAINT

正确答案是C。WM_PAINT的优先级低,是因为系统要避免频繁重绘。InvalidateRect只是把区域标记为无效,WM_PAINT要等到消息队列空闲时才会产生。选B的同学是没弄明白"标记无效"和"立即重绘"的区别。选D的同学忽略了系统可能因为窗口大小变化、遮挡重现等原因主动发出WM_PAINT。

第六题,关于DllMain,以下哪个操作在DllMain中执行时可能导致死锁?

A. 调用一个普通的数学库函数 B. 用EnterCriticalSection进入临界区 C. 调用LoadLibrary加载另一个DLL D. 读写进程的全局配置变量

正确答案是C。DllMain执行时,系统持有进程加载锁,在DllMain里再调用LoadLibrary,就可能因为等待同一把加载锁而死锁。这就是为什么规范要求DllMain只做最基础的初始化和清理。B选项里的临界区操作在DllMain中本身不算规范操作,但相比LoadLibrary,死锁风险不来自加载锁,这个区别做题时要注意。

第七题,安全相关:以下哪个函数最容易导致栈缓冲区溢出?

A.snprintfB.strncpyC.strcpyD.memcpy_s

正确答案是C。strcpy不检查目标缓冲区大小,把源字符串无条件复制到目标地址,一旦源串长度超过目标容量,就会越界写内存。其他几个都带长度限制或安全检查机制。这类安全题的高频变形是给一段特定代码,让你判断漏洞类型,或者让你选择修复方案,核心知识点就是危险函数与安全函数的对照表,背下来能省不少时间。

以上这几个方向的题,做多了会发现知识点就那些,关键是熟练度。错题一定要记录下来,尤其是那些"看似会但选错"的题目,它们往往暴露的是概念理解不够深。

4. 备考策略与实战心得

4.1 高频考点速查表

我把这套笔试题里最高频的考点整理成一个速查表,复习的时候可以对照自测:

考点领域具体知识点考察频率典型出题方式
C/C++sizeof、数组退化、内存对齐高计算题、判断输出
C/C++堆栈区别、new/delete、内存泄漏高代码场景选择
操作系统进程线程区别、切换开销

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询