尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

360校招Windows开发笔试:C++内存、线程同步与安全机制考点拆解

360校招Windows开发笔试:C++内存、线程同步与安全机制考点拆解 准备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包含末尾的\0strlen(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_MACHINEHKLM和HKEY_CURRENT_USERHKCU。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。这道题的坑在于很多人只算字段总长度18918忽略了填充字节和对齐规则。做这类题记住三条对齐规则就够用不用背任何编译器特例。如果题目加了#pragma pack(1)那所有对齐都取1总大小就会变成18这种反差也是常考的变形。第二题判断下面代码的输出char str[] hello; char* p str; cout sizeof(str) endl; // ? cout sizeof(p) endl; // ?sizeof(str)是6因为数组包含结尾的\0sizeof(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/Csizeof、数组退化、内存对齐高计算题、判断输出C/C堆栈区别、new/delete、内存泄漏高代码场景选择操作系统进程线程区别、切换开销
返回列表