☰
嵌入式C/C++笔试高频考点全解析:指针、内存与volatile实战
2026/10/15 7:31:23 网站建设 项目流程

简介:面向C/C++软件开发岗位的笔试真题资料,源自奔图电子软件笔试试题v1.1,集成“大厂面试真题库”常见考点,适合嵌入式、后端开发方向的求职者及面试准备者使用。内容系统覆盖C/C++核心知识模块,包括变量作用域与生命周期、指针使用、整数溢出、结构体定义、类与对象、运算符重载、抽象类、继承、排序算法、单链表、递归函数、操作系统进程与死锁、文件系统、TCP/IP协议、MFC消息映射、图像存储等高频考点,并包含20道单选题、10道判断题、7道填空题,题型结构清晰。文件为docx文档,共1个文件,压缩包大小约34KB,体积精简易于携带。该资料已有285人学习浏览,题目设置满分100分、时限60分钟,贴近真实机考笔试场景;每道题附有简明考点解析,可帮助考生快速定位薄弱点、理解易错概念,尤其适合在面试前进行系统性自测与查漏补缺。

1. 嵌入式C/C++软件笔试:一份真题集背后的能力坐标

嵌入式C/C++软件笔试的真题文档,看起来是一沓题目,实际上是一张能力坐标图。它考的不是背诵,而是四件事:能不能看懂内存、管不管得好生命周期、理不理解编译器替我们做了什么、能不能在资源受限的硬件上用C/C++写出可维护的代码。对刚出校门的人,这套题决定你能不能进面试;对工作两三年的工程师,它暴露的是日常业务里那些“会用但说不清”的底层细节。本文不搬运原题,只把这类笔试最常见的考察面、做题节奏和失分点拆开讲透。适合正在准备嵌入式软件岗位笔试面试的C/C++工程师,也适合用来做团队技术摸底。

2. 嵌入式笔试考什么:五个高频考察面拆解

一套嵌入式C/C++笔试题的结构通常是:语法基础占三成、内存与指针占两成、C++特性占两成、操作系统与嵌入式场景占两成、剩下是编译链接和代码阅读。这不区分校招社招,底层逻辑一致:嵌入式岗位写的是贴近硬件的代码,编译产物直接跑在目标板上,出了内存问题没法像服务端那样热修复。所以笔试设计者优先考察的不是你会不会用某个框架,而是你脑子里有没有一张“代码如何变成机器指令、数据如何在内存里流动”的图。

2.1 指针与内存:笔试里最值钱的二十分

指针和内存是嵌入式C笔试题里出现频率最高的组合。常见的考法是给你一段代码,问输出结果、进程会不会崩、sizeof的结果是多少。要答对这组题,脑子里需要一张明确的分区图:栈区放局部变量和函数调用帧,堆区放动态分配的内存,静态区放全局变量和static变量,代码区放指令。这里最容易被考到的点是“数组名是否等价于指针”和“sizeof与strlen的区别”。

#include <stdio.h> #include <string.h> int main() { char str[] = "hello"; char *p = str; printf("sizeof(str)=%lu\n", sizeof(str)); // 6,包含结尾 '\0' printf("strlen(str)=%lu\n", strlen(str)); // 5,不包含结尾 '\0' printf("sizeof(p)=%lu\n", sizeof(p)); // 8,指针本身占 8 字节(64位平台) printf("sizeof(*p)=%lu\n", sizeof(*p)); // 1,char 类型大小为 1 字节 return 0; }

这段代码对应的是最基础的辨析题:数组在声明时是数组,sizeof计算的是整个数组占用的字节数;但在表达式里它会被隐式转换成指向首元素的指针,所以strlen拿到的只是字符串长度。指针p则不同,无论它指向什么类型,sizeof(p)得到的是地址占用的空间,在64位平台固定为8字节。答题时先写sizeof和strlen的结果,再解释数组名和指针的区别,基本就能拿满这道题的分。

再往后是二级指针和指针数组的辨析。常见题目类似“int *a[10]与int (*a)[10]的区别”,前者是10个int指针组成的数组,后者是指向包含10个int的数组的指针。读这类声明有个口诀:从变量名开始,先向右看再向左看,遇到括号改变方向。在嵌入式代码里,指针数组多用于管理一组外设寄存器地址,数组指针则常见于二维数组的传参场景。建议复习时把指针声明、sizeof结果、数组退化为指针这三个点连成一条线去理解,笔试时不容易被绕晕。

最后是内存泄漏和野指针的判定。典型场景:函数内部malloc了一块内存,返回指针给调用方,调用方正常使用但忘记free,长时间运行后内存持续增长。笔试里不会让你写一个完整的检测工具,但会通过改错题让你指出“谁负责释放”“何时释放”。判断标准就一条:谁alloc谁free,跨模块传递时要在接口注释里写明所有权转移。这个习惯在写驱动和协议栈时尤其重要,因为嵌入式设备没有服务端那么大的内存余量,泄漏十几个字节也可能在几天后触发复位。

2.2 编译链接与宏:藏在预处理阶段的坑

C/C++笔试的第二大考察面是编译链接过程。很多候选人对编译器报错能改明白,但对预处理和链接阶段的细节模糊不清。这部分的考点集中在条件编译、头文件守卫、带参宏和静态库动态库的选择上。嵌入式项目里常有“同一份代码要编译出多个产品版本”的需求,条件编译就是最基本的实现手段。

/* 头文件守卫:防止同一个头文件被重复包含 */ #ifndef __APP_CONFIG_H__ #define __APP_CONFIG_H__ #define SUPPORT_BLE 1 #define SUPPORT_WIFI 1 #define MAX_RETRY_COUNT 10 #if (SUPPORT_BLE && SUPPORT_WIFI) #define ENABLE_DUAL_STACK 1 #endif /* 带参宏一定要加括号,防止表达式展开后运算优先级出错 */ #define SQUARE(x) ((x) * (x)) #define MIN(a, b) ((a) < (b) ? (a) : (b)) #endif

头文件守卫配合条件编译是嵌入式项目里的标配。常见的坑是宏定义里的参数没有整体加括号,例如写成#define SQUARE(x) x * x,调用SQUARE(2 + 3)时被展开成2 + 3 * 2 + 3,结果是11而不是25。笔试里经常把它包装成“以下代码输出多少”的选择题,本质考的是预处理展开后代码的实际形态。做题时建议先把宏展开成普通表达式,再按运算符优先级算,不要盯着宏本身想。

编译链接层面的另一个高频考点是声明与定义的区分。C语言里“变量声明”只引入名字和类型,不分配空间;“变量定义”才分配空间。多个源文件共用全局变量时,在一个文件里定义int g_counter;,在关系到它的文件里用extern int g_counter;声明,这是标准做法。笔试陷阱则出现在“头文件里定义变量”这种情况:如果某个头文件里写了int g_counter;且被两个源文件包含,链接时会报重复定义。遇到这类改错题,先把头文件里的定义改成声明,再在某个源文件里补上定义即可。

2.3 C++核心:从struct到class,从虚函数到智能指针

嵌入式项目里纯C和纯C++都有,但更多的是C为主、在应用层局部使用C++。笔试对C++的考察通常集中在几个点:类的默认成员函数、拷贝与移动语义、虚函数机制、RAII与智能指针。如果你的简历写了C++,至少要能把“为什么析构函数建议声明为虚函数”讲清楚。

虚函数是C++笔试题里的常客。常见考法:基类指针指向子类对象,调用一个普通函数和一个虚函数,问输出顺序。原理上,编译器为每个含虚函数的类生成一张虚函数表,表里存的是函数地址;对象实例里有一个隐藏的虚表指针vptr,指向所属类的虚函数表。当通过基类指针调用虚函数时,程序从vptr找到虚表再跳到对应函数地址,这个过程叫动态绑定。笔试里更进阶的考法是让你解释“构造函数里调用虚函数会怎样”——答案是不会触发动态绑定,因为构造时对象的类型还是当前正在构造的类。

#include <memory> #include <string> class Sensor { public: explicit Sensor(const std::string &name) : name_(name) { // 模拟打开设备:申请一个硬件句柄 } ~Sensor() { // 模拟关闭设备:释放硬件句柄 } Sensor(const Sensor &) = delete; Sensor &operator=(const Sensor &) = delete; Sensor(Sensor &&) = default; Sensor &operator=(Sensor &&) = default; void read() { /* 读取数据 */ } private: std::string name_; }; int main() { // RAII:智能指针在作用域结束时自动调用析构函数 std::unique_ptr<Sensor> sensor = std::make_unique<Sensor>("temp"); sensor->read(); return 0; } // 此处 sensor 析构,硬件句柄被释放

这段代码演示了两件事:一是管理硬件句柄的类应该禁用拷贝、开启移动,避免同一份驱动资源被两个对象持有;二是用RAII包装资源生命周期,在栈对象的析构函数里做清理。回答C++面试问题时,“资源获取即初始化”这六个字是一个很好的切入口,面试官听完会觉得你的C++不是停留在语法层面。嵌入式场景里堆内存本来就紧张,手动new/delete最容易出泄漏,用智能指针配合RAII是笔试和面试的一致取向。

C++另一个常考点是模板和STL容器的使用边界。笔试不会让你手写一个模板容器,但会问“vector和list的区别”“map底层实现是红黑树还是哈希表”,以及“在频繁插入删除的场景下选哪个”。做这类题时先讲复杂度再讲实际开销:vector的随机访问O(1)但插入中间节点需要搬移元素,list的插入删除O(1)但局部性差、缓存友好度不高。嵌入式实时任务里如果要求确定性的时间开销,很多规范会禁用动态内存分配,此时容器要换成预分配的静态池方案。

2.4 操作系统与并发:进程线程那几道必问题

涉及Linux嵌入式开发的笔试题,操作系统部分是稳拿分区域。高频考点包括进程与线程的区别、线程同步的几种手段、死锁的四个必要条件。自从多核处理器在嵌入式设备里普及后,并发题的权重还在上升。做题时建议先判断题目问的是“概念”还是“场景”:概念题直接背定义加对比;场景题需要你顺着“共享了什么资源→哪个操作不是原子操作→怎么加锁”的路径推理。

线程同步的代码题通常要求你分析一段使用互斥锁的代码,指出哪里会死锁。死锁的四个必要条件比较明显,一般不会出纯背题,而是包装成“两个线程分别持有锁A/B,同时等待对方释放”的案例。解题时先把线程的加锁顺序列出来,如果发现锁的获取顺序不一致,这就是潜在的死锁点。

#include <pthread.h> static int shared_counter = 0; static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; void *thread_func(void *arg) { /* 普通变量自增不是原子操作,需要互斥保护 */ pthread_mutex_lock(&lock); shared_counter++; pthread_mutex_unlock(&lock); return NULL; }

上面这段代码把锁的加解锁放在临界区两侧,是标准写法。笔试改错题里常见的问题是:某个线程加锁后提前return,忘了解锁,导致其他线程永久阻塞。看到这种情况,解决思路是先把释放的动作放到所有出口都会经过的位置,或者用RAII锁guard,比如std::lock_guard,把解锁交给栈对象的析构函数。回答时能补上“解锁一定要覆盖所有return路径”这个细节,会比只说“加锁解锁配对”得分高。

2.5 嵌入式特有:volatile、位域、寄存器操作

这部分是嵌入式岗位和其他后端C++岗位笔试最大的区别。纯互联网后端不会考volatile和位域,但嵌入式笔试必考。volatile告诉编译器“这个变量的值可能随时被外部改变,不要用寄存器里的副本做优化”,典型场景是读取硬件状态寄存器、中断服务程序里修改的全局变量、多线程共享变量。笔试的经典问法是“一个全局变量在中断里被修改,主循环里读取它,要不要加volatile”——答案是要,否则编译器会把首次读取的结果缓存下来,主循环永远得不到新值。

寄存器操作是嵌入式笔试的压轴题之一。常见考法是让你写一个“把某地址的寄存器的某个位区域设为指定值”的宏或函数。

#define REG_BASE_ADDR (0x40000000UL) /* 读取32位寄存器的值,volatile 防止编译器优化掉重复读取 */ #define REG32(addr) (*(volatile uint32_t *)(uint32_t)(addr)) /* 把寄存器 base+0x0C 的 bit[11:8] 设为 0x5,其他位保持不变 */ uint32_t reg_val = REG32(REG_BASE_ADDR + 0x0C); reg_val &= ~(0xF << 8); reg_val |= (0x5U << 8); REG32(REG_BASE_ADDR + 0x0C) = reg_val;

写寄存器操作代码时,加volatile的目的在于防止编译器把两次连续的寄存器读优化成一次,这在轮询状态位时会直接导致逻辑错误。笔试中还会进一步问位运算的优先级问题:如果漏写括号,移位和与或的运算顺序可能不符合预期。我一般建议先看书题把“读-改-写”三步写出来,再核对括号和掩码,不要直接在寄存器地址上写一整行表达式。

嵌入式特有的第三个考点是大小端字节序。打印控制、网络协议、存储媒介的场景都会涉及字节序转换。笔试题目一般是“某32位整数0x12345678,在小端模式下内存从低到高排列是什么”。答题方法:记住小端是低字节存在低地址,0x12是最高字节,所以小端排列是78 56 34 12。如果记混了,就写成“小端像我们日常读数字的方向”这种助记符。实际代码里的转换用移位和掩码实现:

uint32_t swap_endian(uint32_t value) { return ((value & 0x000000FFU) << 24) | ((value & 0x0000FF00U) << 8) | ((value & 0x00FF0000U) >> 8) | ((value & 0xFF000000U) >> 24); }

这个函数把字节序颠倒,低字节变高字节、高字节变低字节。笔试如果只问概念,写出小端的排列顺序即可;如果要求手写转换,能用上面的位运算写出来就是满分。

3. 四类题型的应对节奏:从选择题到编程题

同样的知识点,放在选择题、改错题、编程题和简答题里,考察的层次完全不同。选择题考的是“见到能不能认出来”,改错题考的是“出了问题能不能定位”,编程题考的是“从零能不能写出来”,简答题考的是“能不能把原理讲给别人听”。四种题型要有四种节奏,不能一把尺子量到底。

3.1 选择填空题:别只看选项,先动手推演

嵌入式笔试的时间通常紧凑,选择题最怕的不是不会,而是“看着都会、选完就错”。选择题的出题套路有规律可循:选项里至少有一个是“结果对但理由错”,一个是“边界条件没考虑”,剩下两个才是干扰项。做题节奏我一般是这样:先判断考点归属哪个章节,然后不看选项把手里的例子在草稿纸上跑一遍,最后再和选项对比。比如考运算符优先级,先自己把表达式展开,再去看哪个选项匹配你的结果,不要反向猜题。

填空题则更多是补全某个表达式。嵌入式场景里补全寄存器读写、补全位操作、补全中断服务函数的声明。补全代码时要注意题目有没有明确给出编译环境是32位还是64位,这直接影响sizeof和指针字节数的答案。另外填空题的答案要写完整,别只写关键表达式,定义变量的类型有时就是得分点。比如题目要求“将寄存器第3位置1”,写出reg |= (1U << 3)比只写reg = reg | 8更能体现你对位操作的理解。

3.2 改错题:按“内存→指针→生命周期”的顺序排查

改错题是嵌入式笔试拉开差距的题型。适合的思路是分层排查:第一层看内存问题,第二层看指针问题,第三层看生命周期问题。按这个顺序走,绝大多数错误都能定位。内存问题包括越界访问和重复释放;指针问题包括野指针和空指针解引用;生命周期问题包括返回局部变量地址和使用已释放的内存。

/* 找出下面的错误 */ #include <stdio.h> char *get_message(int code) { char buf[32]; snprintf(buf, sizeof(buf), "error:%d", code); return buf; // 错误1:返回了栈内存的地址 } void test(void) { char *msg = get_message(3); printf("msg=%s\n", msg); // 错误2:继续使用已经失效的栈地址 }

上面这段代码是改错题的一种标准形态。虽然编译期没有报错、运行时也许能打印出内容,但这完全是玄学——栈地址在返回后很快会被其他函数复用,任何一次函数调用都可能冲掉里面的数据。正确的修法有两种:调用方传入足够大的缓冲区,或者使用malloc分配堆内存并声明调用方负责释放。笔试答题时建议两句话写出“返回了栈地址”和“释放责任不清”,比只写“指针有问题”得分高。

改错题里另一个高频错误是拷贝字符串时忘记预留结尾的'\0'。memcpy(dst, src, strlen(src))只拷贝了src_len个字节,目标缓冲区末尾没有终止符,后续调用strlen或printf读到的内容是垃圾。正确做法是memcpy(dst, src, strlen(src) + 1),或者直接用strncpy配合手动补'\0'。

3.3 编程题:手写代码的边界条件与鲁棒性

笔试卷子最后通常有一到两道编程题,嵌入式方向以链表操作和字符串处理最常见。手写链表反转是经典题,虽然网络上有很多解法,但考场上要在15分钟内无参考写出来还是需要熟练度。我建议把链表反转、合并两个有序链表、查找链表中倒数第k个节点这三个题练到肌肉记忆。

typedef struct Node { int data; struct Node *next; } Node; /* 反转单向链表:返回新的头节点 */ Node *reverse_list(Node *head) { Node *prev = NULL; Node *curr = head; while (curr) { Node *next = curr->next; // 先保存下一个节点 curr->next = prev; // 指向前一个节点 prev = curr; // 前驱后移 curr = next; // 当前节点后移 } return prev; }

写编程题时最容易失分的是边界条件。面试官或判卷系统会测空链表、只有一个节点的链表、全部节点值相同的链表,很多代码在主流程正确但空链表时直接段错误。上面的代码里while(curr)这行天然处理了空链表——循环体不执行,返回NULL,所以没有问题。手写时建议把“输入为NULL时函数要能安全退出”作为必写检查,再加2分。

编程题的第二类得分点是注释。笔试判分不全是程序跑通才给分,人工批改时会看思路,关键步骤有注释能帮阅卷人快速理解你的分工。我的习惯是每3到5行写一行注释,说明这一步在干吗、为什么这么干,比如上面代码中“先保存下一个节点”就解释了为什么不能直接改curr->next。如果时间充裕,建议在主函数里补上一个简单的测试用例,用printf输出反转结果,证明你考虑过验证环节。

3.4 简答题:用“定义→原因→场景”三段式回答

简答题是嵌入式笔试里最考表达功底的部分,常见问题包括“什么是回调函数”“static在C里有哪些作用”“为什么嵌入式开发常用C而不是其他语言”。这三种题的共同点是没有唯一标准答案,但要得分需要结构清晰。我推荐的模板是“定义→原因→场景”三段式,每段一两句话,控制在6到8行以内。

拿“static在C里的作用”举例,回答可以这样组织:定义层面,static修饰全局变量和函数时改变链接属性,使符号只在当前源文件可见;修饰局部变量时延长存储期,变量从栈区挪到静态区,初始化只执行一次。原因层面,嵌入式代码常把驱动内部的状态变量设为static,避免外部源文件误访问;带static的局部变量适合记录“自上次操作以来”的状态。场景层面,按键消抖的时间戳、协议解析的状态机计数器都用static局部变量。这样答下来逻辑完整,阅卷人不用猜你要说什么。

简答题里还有一类是让你对比两个概念,比如“进程和线程”“堆和栈”“浅拷贝和深拷贝”。对比题也用三段式,但要换成“各自是什么→区别在哪个维度→选型依据”。选型依据往场景上靠:实时性要求高的数据流用线程,需要独立地址空间保护安全性的模块用进程;固定大小的临时数据放栈,生命周期不明确的大块数据放堆。

4. 笔试避坑:五个高频翻车现场和修复思路

笔试里真正拉开差距的不是难题,而是基础题上的细节失分。下面五条是我在带新人和模拟面试中反复看到的翻车现场,每条按“现象→原因→解决”展开,最后补一个应试锚点。

4.1 结构体内存对齐:sizeof结果和直觉不符

现象:计算struct的大小时,按成员字节数相加得到的结果和sizeof不一样,差了几个字节。比如struct { char type; int value; short flag; },按直觉是1+4+2=7字节,但32位平台上sizeof是12。

原因:编译器为了CPU访问对齐,在成员之间插入了padding字节。int成员要从4字节对齐的地址开始,char后面补了3字节;short要从2字节对齐地址开始,int结束后已对齐,short后面还要补2字节,让整个结构体大小成为4的倍数。

解决:一是调整成员顺序,把小字节成员放一起,减少padding数量,上述例子里把short挪到char后面可以让结构体压到8字节。二是按场景使用__attribute__((packed))强制紧凑排列,但代价是访问未对齐字段时,部分MCU会触发硬件异常,而且packed结构体在不同编译器的行为有细微差别。补充一个变体考法:结构体里嵌套结构体时,对齐规则基于嵌套结构体的自身对齐值,不是简单把内部成员字节数相加。做题时把每个成员看成“带对齐需求的小块”,按块去拼,比按字节加准确得多。

4.2 函数返回栈内指针:编译不报错,运行崩在玄学位置

现象:调用一个返回char*的函数,第一次打印正常,多调用几次后数据变成乱码,或者程序在完全不同的位置触发复位。

原因:函数体内定义的数组或局部变量是栈上的空间,函数返回时栈指针已经回退,这块地址理论上已失效,后续任何一次函数调用都可能把它覆盖。打印正常只是运气好,崩溃才是必然。

解决:把缓冲区的所有权明确化。常见做法是调用方传入足够大的缓冲区,让函数往里写数据并返回长度;或者用malloc在堆上分配,同时在函数注释里写明“调用方负责释放”。笔试改错题里如果看到return后面跟着一个数组名或局部变量地址,基本可以判定错误是“返回了栈内地址”。修改时优先选“调用方传入缓冲区”方案,因为嵌入式环境对堆分配的使用很谨慎。应试锚点:把“谁是owner,谁负责释放”写在代码旁边,阅卷人一眼能看到你对内存所有权的理解。

4.3 字符串拷贝漏掉结束符:终端打印出现乱码

现象:memcpy(dst, src, strlen(src))之后,用strlen(dst)得到长度偏大,或者printf打印dst时出现一串乱码。

原因:memcpy只拷贝了字符串内容,没有拷贝结尾的'\0',目标缓冲区里原来的残留数据继续被当成字符串内容读取。

解决:拷贝长度用strlen(src)+1,或者使用strncpy之后再手动在buf[n-1]赋'\0'。strncpy本身有一个坑:如果src长度大于等于n,它不会自动补'\0',所以手动补是必须的。笔试改错题里遇到这类问题,修改时要在代码后面加一句说明“+1是为了覆盖结束符”,表明你理解C字符串的结尾约定。如果题目里同时出现了缓冲区大小检查,提醒自己看一眼是否越界。

4.4 宏参数副作用:看似无害的展开改写变量

现象:SQUARE(++x)得到的结果不符合预期。比如int x=3; int y=SQUARE(++x);,你希望x先自增变成4,然后平方得到16,但实际结果是25,x最终变成了5。

原因:宏定义#define SQUARE(x) ((x)(x))在预处理阶段被展开成((++x)(++x)),x被求值两次,自增也被执行两次。

解决:函数式宏改写成inline函数,既保留性能又避免重复求值;如果必须保留宏,至少做到每个参数用括号包住,并在宏定义上方加注释,提醒调用方“入参不能带有自增、自减这类副作用”。笔试选择题里考察宏展开结果时,先把宏展开写出来再按运算符优先级计算,不要在心里直接套用函数语义。

/* 反例:参数自增时被求值两次 */ #define SQUARE_BAD(x) ((x) * (x)) int x = 3; int y = SQUARE_BAD(++x); // x 变成 5,y 得到 25,而不是预想中的 16

4.5 寄存器操作漏掉volatile:轮询状态读到旧值

现象:主循环里轮询外设状态寄存器,数据显示一直没变化。加上一条打印语句后恢复了正常,去掉打印后问题重现。

原因:编译器认为状态寄存器对应的内存地址没有被程序修改,把读取结果优化进了CPU寄存器,后续循环不再从外设重新读取。

解决:所有指向硬件寄存器的指针都用volatile修饰,例如*(volatile uint32_t *)address。volatile告诉编译器“每次使用这个变量时都从内存读取,不要用缓存的值”。考试里经常考“volatile用在哪些场景”:硬件寄存器访问、中断服务程序里修改的全局变量、多线程共享的变量。写出这三类场景,基本就拿到这个知识点的分数。

笔试答题时还有一个小技巧:凡是读写寄存器、位操作补全,先写一个REG32(addr)这样的辅助宏,把volatile语义封装进去,后续代码都是读-改-写三步,不容易在细节上丢分。我的建议是把这个宏在考前就准备好,笔试时能省下不少时间,也能避免每次重写寄存器访问语句时漏掉volatile。

5. 考前48小时:用一组自测题给自己摸底

刷题到了临近笔试阶段,与其继续刷新题,不如做一轮“自测清单”:不看资料,在纸上过一遍自己能否完整回答下表的每个问题。能不看答案写出来的,说明已经内化;需要翻书的,就是重点复习对象。

知识点自测问题答不出时的补救动作
内存分区画一张程序运行时的内存分区图,标出栈、堆、静态区、代码区各放什么对一段C程序逐变量追归属,直到能不看图画出来
指针数组名和指针的sizeof区别;二级指针用在什么场景手写两个涉及char*的字符串处理函数练习
宏写一个安全的带参宏,解释为什么必须加括号把项目中3个函数式宏改成inline函数,观察差异
C++解释虚函数表和动态绑定原理;智能指针为什么能防止泄漏手写一个包含虚函数的继承小型demo
并发死锁四个条件;互斥锁加锁失败会怎样用伪代码写出两个线程加两把锁的死锁示例
嵌入式volatile适用三种场景;位操作实现寄存器置位和清除写出读-改-写三步操作一段寄存器代码

做完这组自测后,复习收尾动作是限时做一套模拟卷。我习惯把模拟卷的编程题单独计时,每道题控制在15分钟以内,超过时间就先跳过。嵌入式笔试的节奏通常是选择题每道1分钟、填空题每道2至3分钟、编程题15分钟,按这个节奏分配时间,能避免在一道难题上透支后面编程题的答卷时间。

经过这轮摸底,你对自己在哪个知识点上最薄弱会非常清楚。考前最后一天不要再看新题,把自测中答不出来的问题重新过一遍,再把改错题的checklist(内存→指针→生命周期)默写在纸上。这个方法陪我走过多次笔试,也帮团队里的新人做过考前模拟,效果稳定。最后一条经验:笔试后的复盘比刷题更重要,每次考完把没做出来的题记到错题本里,标注失分原因是知识盲区还是答题节奏问题,下次备考直接从错题本开始。希望帮到你。

本文还有配套的精品资源,点击获取

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

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

立即咨询