尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

美图2017秋招C++笔试题解析:从基础考点到工程实践

美图2017秋招C++笔试题解析:从基础考点到工程实践 校招笔试这事说穿了就是一场“基础能力体检”。美图2017秋招C开发工程师的笔试卷子我印象很深不是因为题目多偏多难恰恰相反它是一套非常“正统”的C校招卷。全卷基本围绕C核心语法、内存管理、数据结构与算法、操作系统与网络基础展开没有那种炫技式的偏题怪题。如果你认真准备过C面试这套题里的大多数考点你应该都见过。它考的不是你会不会背C八股文而是你写代码的底层功底和工程素养。整张卷子的难度梯度也很明显选择题照顾知识面简答题考察理解深度编程题拉开区分度。无论你是正在准备校招的在校生还是打算跳槽的C开发把这类试卷吃透了都等于做了一次系统的知识体检。1. 试卷整体思路与考察点设计拆解1.1 题型分布与考察维度美图这套C岗笔试卷大致可以分成三个模块选择题单选多选、概念简答题、编程题。每个模块的定位完全不同考察的维度和能力模型也有明显区分。选择题覆盖面很广C语言基础、数据结构、操作系统、网络、设计模式都可能涉及重点考察你的知识面是否完整。这一部分的特点是量大题多平均一题一分钟内要出答案非常考验基础概念的熟练程度。简答题侧重于对某个知识点的深入理解。比如让你手写一个线程安全的单例模式、说明虚函数表的内存布局、解释内存泄漏的检测方法等。这类题没有标准到唯一的答案考察的是你是否有真实的工程实践积累而不是看了几篇博客就能答好的。编程题一般2到3道涵盖链表操作、字符串处理、动态规划、数值计算等高频题型考察算法功底和代码风格。编程题是最能拉开差距的部分基础扎实的同学二十分钟能AC两道基础薄弱的同学可能一道题就卡半小时。这种结构放在今天看依然是主流配置。美图的特色在于选择题里C基础部分的占比明显高于其他互联网公司尤其是涉及到const、指针、内存布局、STL容器底层的题目考得非常细致。这跟美图的核心业务强相关——图像处理引擎是重底层C开发的对内存效率、指针操作、资源管理的要求极高笔试自然要优先筛选出真正懂底层的人。1.2 校招筛选背后的逻辑先来说一个可能被很多人忽略的点校招笔试不是用来选“最聪明的人”的而是用来筛掉“基础不牢的人”。一套合格的C笔试卷子必须能把三类人区分开。第一类C语法背得熟但没写过真实项目遇到内存管理和并发问题就露馅。第二类会调库熟悉常见算法题套路但对编译链接、运行时行为没有概念。第三类基础扎实有清晰的编译执行心智模型能自己排查问题、写可靠代码。美图2017年这套题恰恰是按照这个逻辑设计的。你会发现选择题里几乎每个C考点都对应一个工程中的真实场景比如const用于接口约束、指针用于资源管理、结构体内存对齐影响通信协议解析、vector扩容影响迭代器的稳定性等。如果你只会背概念而没有真正写过程序这些题会做得很别扭反过来如果你平时写代码时就注意过这些问题做起来会非常顺畅。这套题的另一个特点是考点分布广但每个考点的深度是可控的。它没有考协程、没有考编译器优化也没有考那些只有在生产环境才会遇到的极端case而是老老实实地把C开发日常工作中最常用的知识点“全面覆盖”了一遍。这给了备考者一个明确的信号把地基打牢比追求偏门考点划算得多。2. C核心考点逐题拆解2.1 语言基础与关键字考察C语言基础是这套试卷的重头戏其中const、static、extern、volatile这几个关键字几乎年年出现。先说const与constexpr。2017年C17已经推出了两年但校招题里依然会问“constexpr是哪个C版本引入的”这类问题。答案是C11。constexpr与const最大的区别在于constexpr要求编译期求值至少在语言层面鼓励这样做可以用来定义真正的编译期常量而const只是“只读”的语义约束。举个例子const int n 10;数组定义时可以用但如果你写const int n func();就没法用作模板参数或数组大小。constexpr则强制要求func()在编译期可算否则直接编译报错。这个区别在笔试里经常以选择题形式出现很多人在“constexpr函数能不能包含循环”和“constexpr变量是否一定在编译期求值”上栽跟头。这里要说得更准确一点C11里constexpr函数是被限制得比较严格的C14开始放宽允许包含循环和局部变量。顶层const与底层const也是个经典区分。int* const p表示指针本身不可变这是顶层constconst int* p表示指针指向的对象不可变这是底层const。函数重载时顶层const会参与函数签名匹配吗不会只有底层const才会影响重载决议。这道题看着简单实际在判断代码合法性时非常容易混比如void f(int*)和void f(int* const)同时存在就会导致重定义错误而void f(int*)和void f(const int*)则是合法的重载。关于关键字这块我个人的经验是别死记硬背把const、static、extern、volatile、mutable、explicit这些关键字每个都找一个真实使用场景想明白“没有它行不行”能写出例子才算真正掌握。比如static在类成员中表示所有实例共享在局部变量中表示生命周期延长到程序结束在文件作用域中表示限制外部链接性这三个用法差别很大但底层逻辑一脉相承。把底层的“存储期”和“链接性”理解透了这类题就是送分题。2.2 内存管理指针、引用与生命周期这一块是C笔试的失分重灾区。美图的试卷里关于指针的题目几乎没有那种“int *p NULL; 判断p是否等于0”的初中题而是更偏向工程场景。new/delete与malloc/free的区别绝对必考。四个层面的区别要梳理清楚前者是运算符后者是库函数前者在分配内存后会调用构造/析构函数后者不会前者返回类型安全的指针后者返回void*需要强转前者可以被运算符重载后者不能。更深一层的问题是“new[]和delete不配对用会怎样”。答案是行为未定义在MSVC下可能不崩在GCC下可能崩原因在于不同编译器对“new[]时是否额外分配一个int记录元素个数”的实现不同。这类题没有标准答案考察的就是你有没有真正调试过这类问题、有没有被段错误折磨过的经验。内存布局题也是高频考点。函数栈帧、堆、全局区、常量区、代码段的分布以及一个类对象在内存里的真实布局虚函数表指针在哪里、成员变量如何对齐、静态成员放在哪里这些如果只看书不用内存诊断工具去验证很难有具象感知。我建议准备校招的同学在笔试前用gdb或Visual Studio的内存窗口看一下自己写的小程序中各个变量的地址分布观察栈地址向下生长、堆地址向上生长亲眼看到常量区字符串共享的行为比刷十道题都管用。引用和指针的区别也常考。一个常见的表述是“引用是一个变量的别名必须在定义时初始化之后不能再改绑到其他对象指针可以重新赋值可以为空”。这个说法没错但要更进一步理解引用在底层实现上通常就是一个地址的拷贝只是语法层面限制了使用方式。笔试里遇到“引用能否为空”这种极端题时理论上讲引用不会为空但实际工程中可以通过把空指针解引用并绑定引用导致未定义行为这个细节属于加分项能说出来会显得你对底层理解更深。RAII资源获取即初始化是C内存管理的核心思想笔试简答题如果问“如何避免内存泄漏”最好的答案不是“记得delete”而是“使用智能指针把资源管理和对象生命周期绑定”。unique_ptr独占所有权、shared_ptr共享所有权、weak_ptr解决循环引用这三者的适用场景要能用一句话说明白。shared_ptr的底层是引用计数要注意循环引用会导致计数永远不为零内存无法释放这是笔试特别喜欢考的坑。2.3 STL容器与底层实现STL在C笔试里绕不开。美图这套卷子里有一道vector扩容的选择题问“vector在扩容时元素如何迁移迭代器为什么会失效”。答这类题你要知道三点vector通常以2倍GCC或1.5倍MSVC的因子扩容扩容会经历“分配新内存—移动C11及之后或拷贝旧元素—释放旧内存”三个步骤一旦发生扩容所有指向旧内存的迭代器、指针、引用全部失效。注意是“全部失效”不是“部分失效”。这个知识点在工程中也很实用比如在一个循环中往vector里插入元素时要特别小心最好用下标而不是迭代器。string字符串数组初始化的题也出现过。char str[] hello;与char* str hello;的区别本质上是“栈上可修改的字符数组”和“只读常量区指针”的区别。前者在栈上分配了6个字节含结尾的\0内容可以修改后者指向字符串常量区一旦试图修改str[0]就会触发段错误。这道题其实就是考C字符串字面量的存储位置属于“知道就秒杀不知道就蒙圈”的类型。现代C笔试里还会问你std::string的底层实现通常涉及SSO小字符串优化短字符串直接放在对象内部避免堆分配长字符串才使用堆内存这个优化在C11以后被广泛采用答出来能加分。map与unordered_map的选择题也值得展开。红黑树实现的map能保持有序遍历增删查都是O(logn)哈希表实现的unordered_map平均O(1)但无序且对key类型有hash函数要求。试卷里经常给一个场景让你选容器核心判断标准就是需不需要有序性、key有没有现成的hash函数、数据规模多大。比如按字典序输出词频统计结果用map就比unordered_map方便很多因为遍历时天然有序。还有一类题是反转考察给你一段使用了list的场景问为什么不用vector或者反过来。这背后的核心区别在于vector是连续内存、随机访问O(1)、插入删除O(n)但局部性好list是双向链表、随机访问O(n)、插入删除O(1)但每节点有额外指针开销、缓存不友好。笔试里遇到这类题把“连续存储vs链式存储”的差异答清楚就能拿大部分分数。2.4 设计模式与工程实践热词里有“c 设计模式”校招笔试里设计模式一般不会考太复杂的但单例模式是例外几乎必考。美图的卷子里出现过手写线程安全单例的简答题这个题我印象很深。手写单例应该分三个版本递进理解。懒汉式最简单的写法是函数内静态局部变量加一个判空但这在多线程下有数据竞争。改进版是DCL双重检查锁加volatile但细节很多容易写错。C11及之后最推荐的做法是直接利用局部静态变量的初始化线程安全性代码非常简洁class Singleton { public: static Singleton getInstance() { static Singleton instance; return instance; } Singleton(const Singleton) delete; Singleton operator(const Singleton) delete; private: Singleton() {} ~Singleton() {} };这段代码里有两个细节值得关注一是删除拷贝构造和赋值运算符防止外部复制单例二是构造函数和析构函数设为私有防止外部自行创建。这两个细节在笔试中能写出一个就算答得不错两个都写出来说明你真的理解单例的约束条件。除了单例工厂模式、观察者模式、策略模式在校招中也偶尔出现。我的建议是不要背UML图而是每个模式准备一个代码级的例子。比如观察者模式用std::function和vector实现一个回调列表策略模式用函数指针或lambda作为策略参数都能直接用代码说明“这个模式解决什么问题”。2.5 函数指针、回调与语言细节热词里有“c回调函数例子”这部分在笔试里通常以选择题或简单编码题的形式出现。C回调函数的本质是“把一段代码作为参数传递给另一段代码”使其在合适的时机被调用。常见的实现方式有函数指针、std::function、lambda表达式三种。函数指针的声明语法是个易错点。void (*fp)(int)是一个指向参数为int、返回值为void的函数的指针注意括号的位置。如果用typedef或using别名会更容易读。C11引入了lambda表达式后函数指针的使用场景很多被lambda替代了但笔试仍然会考基础的函数指针语法尤其会结合qsort的compare函数或某个排序算法的比较器来出题。lambda有两个细节经常被忽略。一是捕获方式[]值捕获和[]引用捕获值捕获的变量在lambda体内是拷贝的引用捕获需要保证引用对象的生命周期覆盖lambda的调用时机。二是lambda的类型是编译器生成的匿名函数对象不是原始函数指针所以把lambda传给一个接受函数指针的接口时空捕获列表的lambda可以隐式转换有捕获的lambda则不能。这道题在考“C11特性”时经常出现。还有一个语言细节是虚函数的默认参数。基类指针指向派生类对象调用虚函数时函数体走派生类版本但默认参数用的是基类定义的默认参数因为默认参数在编译期就固定了不参与动态绑定。这个反直觉的特性在选择题里出现过多次属于刷过就有分、没刷过必错的题目。3. 数据结构与算法解题思路实录3.1 链表高频题型与代码框架编程题里链表是绝对的常客因为链表操作非常考验指针操作的严谨程度。最重要的一道题是反转链表迭代版递归版都要能写。迭代版需要三个指针prev、cur、next。循环里先保存next再让cur-next指向prev然后三个指针一起前移。很多人在边界条件上出错比如空链表、单节点链表。我习惯写的时候先判断head nullptr || head-next nullptr直接返回head再进入主循环。递归版的思想是“先反转后面的节点再让当前节点的下一个节点的next指向当前节点”代码更短但对初学者来说更容易绕晕。笔试时如果对递归版不够熟直接用迭代版完全没问题面试官更看重你的代码能不能跑通而不是用哪种写法。判断链表是否有环用的是快慢指针一个走两步一个走一步如果有环必然相遇。进阶问法是“如何找到环的入口”解法是先让快慢指针相遇然后把快指针放回头节点两个指针各走一步再次相遇的位置就是环的入口。这个结论背后是数学推导从头节点到环入口距离a环入口到相遇点距离b快指针在环内绕了k圈则有2(ab)abkL其中L是环长推导出a(k-1)L(L-b)说明从头节点到入口的距离等于从相遇点继续走到入口的距离。这个推导不复杂但笔试时能现场推出来的人不多建议提前记结论。链表题的避坑心得我有几条实战经验循环条件统一写成while (p ! nullptr)而不是while (p)笔试时不容易看错。反转和删除类操作先画图再写代码图画对了代码就不容易错。需要返回新头节点的操作用哑节点dummy node技术能简化边界处理。写完后自己人肉走一遍一到两个典型用例比如5个节点的链表反转确认中间指针没有断链。3.2 高频算法排序、快速幂与二分排序算法的考察方式一般有两种让你手写某种排序或分析某种排序的复杂度。美图的卷子里冒泡排序和选择排序都被问到了这是最经典的入门级别但别低估这类题。冒泡排序可以做个简单优化如果某一趟没有任何交换说明已经有序可以直接退出。这个优化代码只加一个flag属于典型的“会写不难难的是想到优化”。快速幂算法也是一道经典题求解x的n次方朴素算法的时间复杂度是O(n)快速幂用二分思想把复杂度降到O(logn)。核心思路是n为偶数时x^n(x^(n/2))^2n为奇数时x^nx*(x^((n-1)/2))^2。用迭代写法要小心二进制分解每次循环判断当前位是否为1为1就乘上basebase每次自乘为下一位的幂。long long fastPow(long long base, long long exp, long long mod) { long long result 1; base % mod; while (exp 0) { if (exp 1) { result (result * base) % mod; } base (base * base) % mod; exp 1; } return result; }注意取模操作要每一步都做防止溢出。这个模板可以直接套用在一大批“求大数幂的模”的题目里。二分查找也是高频考点热词里虽然没有直接提但“c八股文”和“c面试题”经常覆盖这个内容。二分写法有个隐藏的坑mid left (right - left) / 2;不要写mid (left right) / 2;因为后者在极端情况下会整数溢出。这个细节在笔试中经常作为选择题的隐藏陷阱出现。动态规划的题在这类笔试试卷中也经常出现典型的有最长公共子序列、背包问题、最长上升子序列。很多同学对动态规划最大的误区是觉得它是一个“套路”上来就开始写转移方程。我的经验是先想清楚dp数组的含义下标是什么、值是什么再想递推关系最后想初始条件。这三步顺序不能乱。dp数组的含义定义对了递推关系自然就出来了。考场时间紧迫动态规划题一定先写清楚状态定义再动手否则大概率写一半推倒重来。3.3 编程题的读题与答题策略编程题最怕的不是不会做而是会做却拿不到分。我总结了几条血泪经验都是实际笔试中验证过的。先读输入输出格式再读题目描述。很多题目的坑在输入输出格式里比如“第一行一个整数T表示测试用例数”“多组输入直到EOF”搞错输入输出格式代码写得再对也过不了。写代码时一定要处理边界条件。空数组、单元素、全同元素、最大/最小数值这几个边界样例必须在心里过一遍。很多笔试平台只给一组示例输入示例过了不代表边界过了你永远不知道评测数据里藏着什么。用C写算法题时优先用cin/cout并关闭同步ios::sync_with_stdio(false); cin.tie(nullptr);这样会比默认的cin快很多。笔试环境用scanf/printf也行但千万别混用导致缓冲区错乱。另外变量命名尽量用有意义的名称比如while (right n)不要写while (r n)。笔试阅卷某种程度上看的是工程素养清晰的命名说明你平时写的是“工程代码”而不是“做题代码”。如果一道题卡住超过20分钟先跳过去做后面的不要死磕。笔试的时间分配比单题正确率更重要。先把有把握的分拿满再回头啃硬骨头。4. 操作系统与网络考点盘点4.1 进程、线程与并发操作系统部分进程与线程的区别属于必考。核心区别是进程是资源分配的基本单位线程是CPU调度的基本单位。同一进程内的线程共享地址空间和资源而进程之间地址空间相互隔离。答题时如果能补充“进程切换开销比线程切换大因为虚拟地址空间的切换涉及页表切换等”这样的细节会明显在阅卷人那里加分。死锁的四个必要条件互斥、占有且等待、不可剥夺、循环等待也是必背内容。如果要答得更好可以顺带说一句破坏任意一个条件即可预防死锁比如资源一次性分配破坏占有且等待、可剥夺资源破坏不可剥夺、资源编号按序申请破坏循环等待。多线程部分C笔试常问互斥锁和条件变量。std::mutex配合std::lock_guard是RAII管理锁的标准写法但要注意如果函数里需要多个锁且锁的获取顺序在所有线程中不一致就可能引发死锁。规范做法是用std::lock同时锁多个互斥量或者约定一个全局的加锁顺序。条件变量典型的使用场景是生产者消费者模型。生产者往队列里放数据后调用notify_one消费者在队列空时调用wait等待。这里有个经典坑wait必须与互斥锁配合且要有判断条件因为wait可能被虚假唤醒。正确的写法是std::unique_lockstd::mutex lock(mtx); cond.wait(lock, []{ return !queue.empty(); });第二个参数是一个返回bool的谓词wait会先重新获取锁再检查谓词是否为真为真才继续执行否则继续等待。这个写法是标准写法笔试时如果让你写生产者消费者模型一定要把这个细节写出来。热词里有“ABA问题c”这个概念也值得展开。ABA问题常出现在无锁编程中线程A读取到共享变量值为A之后线程B把它改成B又改回A线程A再次比较时发现值还是A就误以为数据没有被修改过。解决ABA问题的常见方案是引入版本号或标记每修改一次版本号加一。在C的std::atomic中可以用带标记的自定义结构体来避免。笔试遇到这个考点能说出“CAS 版本号”基本就过关了。4.2 网络基础TCP/UDP与HTTP美图这类C岗的笔试卷网络部分一般占3到5题重点集中在TCP协议。TCP三次握手四次挥手和状态转换图属于送分题。三次握手SYN、SYNACK、ACK四次挥手FIN、ACK、FIN、ACK。注意TIME_WAIT状态一定出现在主动关闭连接的一方持续时间为2MSL原因有两个一是保证最后的ACK能到达对端如果对端重发FIN这边还能回应二是让旧连接的重复报文段在网络中自然消失避免干扰新连接。笔试里常问“为什么TIME_WAIT要等2MSL”能答出这两点就是完整答案。TCP和UDP的区别也是高频。面向连接vs无连接、可靠传输vs尽力而为、有序vs可能乱序、字节流vs报文、头部开销20字节vs8字节这些对比要能脱口而出。HTTP协议偶尔会考比如GET与POST的区别、状态码含义。C开发如果做后端服务HTTP层考得会比较浅但基本的状态码需要知道200表示OK301表示永久重定向400表示请求错误404表示资源未找到500表示服务器内部错误。这套试卷里印象中有一个选择题给了一组状态码让你判断哪个是“资源未找到”答案是404。网络部分的复习建议把TCP三次握手、四次挥手过程配合Wireshark抓包看过一遍真正理解了重传和乱序记忆会非常牢固而不是死背状态名。校招笔试不会考复杂的网络算法但基础概念一定要扎实。5. 常见失分点与避坑经验5.1 易错题速查表我把这类C校招笔试卷中最容易错的几个考点整理成一张速查表笔试前扫一眼非常有救急作用考点易错点正确答案或要点const char* 与 char* const混淆指向关系const char* 指向常量的指针char* const 常量指针constexpr引入版本认为是C14C11引入C14放宽限制new[]/delete不配对觉得只是内存泄漏行为未定义可能崩溃程序内存布局栈和堆位置写反栈高地址向下生长堆低地址向上生长类内static成员忘记类外定义C17前需类外定义C17后可内联虚函数默认参数认为是动态绑定默认参数是静态绑定虚函数动态绑定两者分离vector扩容后迭代器觉得会自动更新迭代器全部失效需重新获取浅拷贝与深拷贝只想到指针成员涉及资源管理、拷贝构造、赋值运算符、移动构造这个表里值得单独说明的是“浅拷贝与深拷贝”这条。如果一个类含有指针成员默认拷贝构造函数只是拷贝指针的值导致两个对象指向同一块堆内存析构时发生双重释放。正确的做法是自定义拷贝构造函数和赋值运算符或者直接使用智能指针成员。这是C面试里非常经典的坑几乎每个候选人都被问过笔试自然也喜欢出。还有一个易错点是memset与构造函数的关系。memset(this, 0, sizeof(*this))在C中是一个危险操作如果类里有虚函数或非POD成员这么做会直接破坏对象的内存布局导致虚函数表指针被置空调用虚函数直接崩溃。看到这个写法基本可以直接判断代码有严重问题。5.2 考前模板准备清单以一个过来人的经验去笔试前我建议在脑子里预装几个模板而不是现场临时想。这些模板不需要背得一字不差但要能快速默写出来。单例模式C11静态局部变量版本写法快排/归并排序手写模板二分查找模板注意mid left (right - left) / 2链表反转、链表判环模板快速幂模板实现一个线程安全的队列互斥锁加条件变量。其中链表反转和二分查找这两道题在C岗笔试中的出现概率极高甚至可以说是几乎必考。单独再强调一次二分的边界问题while (left right)和while (left right)对应的区间定义完全不同写之前先明确自己用的是闭区间还是开区间然后保持一致。不要写到一半才想起来改循环条件改来改去最容易出bug。编程题还有一个容易被忽略的点函数签名和变量命名。笔试阅卷某种程度上看的是工程素养。用void reverseList(ListNode* head)这种清晰的命名比void f(ListNode* h)更能说明你平时写的是“工程代码”而不是“做题代码”。5.3 考场实战技巧最后说几个考场上的实用技巧每一条都是我自己踩过坑换来的。关于时间分配如果笔试总时长90分钟我会把选择题控制在30分钟以内简答题控制在20分钟以内剩下的40分钟全部留给编程题。选择题太磨叽是大忌一道题超过两分钟不会就标记出来跳过去最后还剩时间再回头想。关于编译环境很多校招笔试平台用的不是本地的IDE而是网页编辑器。这种情况下不要依赖自动补全考前几天就要主动切换到记事本级别的编辑器去写代码培养手写代码的能力。另外写完代码后要自己人肉模拟几组输入输出因为网页编译器不一定能调试。我现在还记得一个严重的教训某次笔试在本地IDE跑得好好的代码复制到网页编辑器后因为缺了一个头文件无法编译白白折损了一道编程题的分。关于“留白”简答题如果不会千万别空着。哪怕是瞎猜也要把自己知道的相关概念都写上去校招笔试的简答题不是按“对错”评分而是按“采分点”评分你写到“内存泄漏”“RAII”“智能指针”这些关键词都能拿分。这条经验在多次笔试里帮我捡回过不少分。关于多选题如果拿不准某一个选项宁可少选也不要错选。多数笔试平台多选题的规则是少选得部分分错选得0分。这个策略在分数上很划算但很多人不知道非要追求选全结果连部分分都没拿到。我个人在备考各种C校招笔试时的最大体会是C是一门注重积累的语言笔试题目再花哨考的还是你对内存、对对象生命周期、对并发同步这些核心概念的理解。这套试卷放在今天看依然有很强的参考价值它没有刻意追求偏题怪题而是老老实实地把C开发日常工作中最常用的知识点从基础到进阶全部过了一遍。你现在拿着这些考点去检查自己的知识盲区效果可能比盲目刷十套新题都好因为新题堆砌概念的太多反而不如这种“常规考法”更能测出真实的语言功底。最后再分享一个小技巧笔试前把C标准库容器的底层实现vector的动态数组、map的红黑树、unordered_map的哈希表在脑子里过一遍你会发现很多选择题根本不需要“猜”而是直接“推导”出答案。祝备考顺利。
返回列表