ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

PTA C语言理论考错题地图:语法判定与高频陷阱

PTA C语言理论考错题地图:语法判定与高频陷阱

简介:面向准备C语言程序设计考试的学生与自学者,这份PTA理论考练习文档聚焦基础语法判断与选择题的自测场景,帮助读者在考前系统核对概念、定位易错点。内容围绕编译预处理命令、main函数入口、语句结束符、变量与内存存储单元、单目运算符、表达式合法性、赋值运算符优先级、scanf与printf格式控制、for循环结构、复合语句与空语句等高频考点逐题给出答案,并配合逐条知识点详解,说明每个判断的依据而不只停留在对错。压缩包内共1个docx文件,约345KB,为可直接打开查阅的Word试题集,按HW02、HW03等题组顺序编排,题干与答案相邻排布,便于打印或边看边记。目前已有1897人学习下载。读者可借此获得一份覆盖C语言基础语法易错细节的题库,通过对照答案与讲解自查运算符优先级、输入输出格式匹配、循环累加等常见误解,适合课堂练习与考前突击使用。

1. 一份 PTA 理论考错题地图长什么样

PTA 理论考这部分题,和能敲进编译器跑一遍的编程题完全是两种不同的考查方式:判断题和选择题给的是一段段代码片段,答案对错全押在脑子里那套语法判定规则准不准上。把整份题库摊开重排,会发现它反复考的其实只有五束东西:词法与语句结构、表达式与运算符、输入输出与字符处理、流程控制,以及散落在这四块边界上的易漏点。适合两类人:正在准备 C 语言理论考的,和代码写得动但一遇到「以下程序段是否符合语法」就凭感觉蒙的。理论考不考你能不能跑通,考你能不能在没编译器的时候判断这段代码会不会炸。

2. 词法与语句地基:预处理、分号、声明顺序与单目运算符

这一章对应题库里最前面那批题,看着最基础,但丢分往往也丢在这里。原因不复杂:这些规则平时写代码时被 IDE 帮你兜住了,自动补全、波浪线提示、编译报错,全都在替你判断;到了纸面判断题上,判断的人换成你自己,边界一旦记模糊,就只能靠语感。语感在这类题上基本靠不住。

2.1 编译预处理命令与 main 函数的两个硬约束

#include <stdio.h>是一个编译预处理命令,判定特征有三个:以#开头、独占一行、在编译之前由预处理器处理。预处理的工作方式是把stdio.h的内容原地展开到当前文件里,展开完才轮到编译器干活。所以它不产生运行期代码,也不参与运算,行尾按惯例不写分号——写了多半也不报错,但规范化写法是不写。

stdio.h这个头文件里放的是printf、scanf、getchar、putchar这些函数的声明。少了这一行,编译器在遇到printf时会认为这个函数没有被声明过,行为就不确定了。

main 函数有两条硬约束,题库反复在这上面出题。第一条:一个可以独立运行的程序,有且只有一个main()函数,它是程序入口。第二条更爱考——C 程序不是从源文件的第一条语句开始执行的,而是从main()函数体的第一条可执行语句开始。源文件开头那些#include、宏定义、函数声明、甚至写在main前面的其他函数定义,都不属于「执行起点」。

说法判定理由
程序从源文件第一行开始执行错起点是 main 函数体第一条可执行语句
程序从 main 的第一条可执行语句开始执行对注释、空行、声明不算可执行语句
一个程序可以有多个 main错独立程序有且只有一个入口
一个程序可以没有 main错缺入口,链接阶段就过不去

提示:判断题里出现「任何」「必须」「只能」这类绝对化措辞时,先别急着判错,回到规则本身核对一遍。C 语言的硬规则确实不少,绝对化措辞只是提高警惕的信号,不是判错依据。

2.2 分号、复合语句与空语句

C 语言里语句以分号结束,这条规则本身没难度,难度在分号的三种不同身份上。第一种是普通语句的结束符,sum = sum + i;后面那个。第二种是复合语句的边界,一对大括号{}把多条语句括起来,语法上整体算作一条语句,所以if、for、while后面的循环体或分支体如果是多条语句,必须用大括号包起来;包起来之后,它对外就是「一条语句」,不需要在右大括号后面再加分号。第三种最容易被忽略——单独一个分号构成空语句,它什么也不做,但它是合法的、完整的语句。

空语句单独出现没危害,混进控制结构里就是灾难。下面这段代码是理论考里最经典的陷阱之一:

int i, sum = 0; /* 注意 for 后面这个分号:它构成一条空语句,成了整个循环的循环体 */ for (i = 1; i <= 10; i++); { sum = sum + i; /* 大括号块和 for 无关,只执行一次,此时 i 已经是 11 */ }

这段代码跑完sum是 11,不是 55。逻辑上拆开看:for的循环体是那个空语句,循环老老实实跑了 10 次,每次什么也不干,跑完之后i停在 11;后面的大括号块是一条独立的复合语句,顺序执行一次,sum = 0 + 11。要改成累加 1 到 10,得把分号删掉,让大括号块成为循环体。写循环时养成一个习惯:for那一行结尾看到分号就停下来确认一下,这是踩坑率最高的位置之一。

复合语句还有一条判定要点:因为它在语法上被视为一条语句,所以它可以出现在任何「只允许一条语句」的位置。if (x > 0) { a = 1; b = 2; }是合法的,两个赋值被大括号打包成一条。反过来,if (x > 0) a = 1; b = 2;里只有a = 1受条件控制,b = 2是无条件执行的,这是另一类高频错题。

2.3 变量必须先声明后使用

k = 1; int k;这样的程序段不符合 C 语言语法,原因是变量k在被赋值时还没有声明。C 语言要求先声明后使用,声明的作用是告诉编译器「这个名字存在,类型是这个,给它留一块空间」。声明之后,这个变量就代表内存中的一个存储单元,对变量的读写就是对这个存储单元的读写——这条判定在题库里以「C 程序中定义的变量,代表内存中的一个存储单元」的形式出现,是对的。

正确顺序只有一种写法:

int k; /* 先声明:告诉编译器 k 是 int 类型,分配存储单元 */ k = 1; /* 再使用:把 1 写进 k 对应的存储单元 */ /* 或者声明的同时初始化,效果一样 */ int m = 1;

参数说明:int k;是定义性声明,既声明名字又分配空间;k = 1;是赋值表达式语句,要求k已经可见。如果在函数内部写k = 1;而前面没有int k;,编译器会报「未声明的标识符」,这不是警告,是错误。

同一节里还有一条关于单目运算符的判定:单目运算符只需要一个操作数。++、--、!、-(取负)、sizeof都是单目,+、-、*、/、%在双目语境下需要两个操作数。判断这类题不用背表,抓住「单目 = 一个操作数」这个定义就够了:fahr++只有一个操作数fahr,是单目;a + b有两个操作数,是双目。

3. 表达式与输入输出:优先级、整数除法、赋值陷阱与格式对齐

这一章是整份题库里失分最集中的区域,因为这里的规则彼此咬合:优先级影响求值顺序,类型影响运算结果,格式串影响输入输出能不能对上。任何一环记错,整道题的结论就反了。复习时不要孤立地背某一条,要把它们串成一条链:先看操作数类型,再看运算符优先级,最后看格式串匹配。

3.1 运算符优先级与隐式乘法的缺失

赋值运算符的优先级低于算术运算符,这一点决定了n = 10 + 2先算加法再赋值,n得 12。题目里有一种反向考法:假设赋值优先级比算术高,问n是多少。按假设走一遍,n = 10先执行,得到 10,然后+ 2无处可去,整段不成立。这类「假设规则反过来」的题,考的是你能不能按给定前提推演,而不是考真实规则,读题时盯住「假设」两个字。

比优先级更容易错的是运算符缺失。5(fahr-32)/9这种写法在 C 语言里不成立,因为 C 没有隐式乘法,数字和括号之间必须显式写出*。题库里这类题在标注上有出入,复习时以规则为准:任何两个操作数之间必须有运算符,5 * (fahr - 32) / 9才合法。写温度转换这类公式时,5.0 * (fahr - 32) / 9.0是标准写法,用5.0和9.0而不是5和9,是为了让整个表达式按浮点运算走。

另外两条合法性判定经常一起出现。x + y = 22不合法,因为赋值号左边必须是可修改的左值,x + y是一个表达式结果,没有对应的存储单元,不能作为赋值目标。x % 2在x声明为double时不合法,因为取模运算符%只接受整型操作数。

表达式前提是否合法原因
5 * (fahr - 32) / 9int fahr;合法运算符完整,整型运算
5(fahr - 32) / 9int fahr;不合法缺乘号,C 无隐式乘法
x + y = 22int x, y;不合法赋值左侧不是左值
x % 2double x;不合法%只接受整型
n = 10 + 2int n;合法加法先于赋值

3.2 整数除法与类型提升

整数除法是这份题库里出现频率最高的知识点,没有之一。规则只有一条:两个整型操作数做除法,结果还是整型,小数部分直接丢弃,不四舍五入。由此推出两个结论:1 / n在n的绝对值大于 1 时恒为 0,因为真实结果在 0 和 1 之间,取整后是 0;1 / 2得 0,不是 0.5。

要让结果带小数,必须让参与运算的至少一方是浮点类型。写法上有三种,效果一样:

int i, n = 4; double a = 1.0 / i; /* 1.0 是浮点字面量,i 被提升为 double */ double b = 1 / (double)i; /* 强制转换,把 i 临时转成 double */ double c = (double)1 / i; /* 转换分子,同样触发浮点除法 */ /* 反例:两边都是 int,结果先取整再赋给 double,得到 0.000000 */ double d = 1 / i;

参数说明:类型提升的规则是「低精度向高精度靠」,int和double混算时int先转成double。所以关键不是结果变量声明成什么类型,而是除法执行的那一刻两个操作数是什么类型。很多题的陷阱就设在这里:结果变量是double,但除法两边都是int,赋值发生在取整之后,白搭。

求1 + 1/2 + 1/3 + ...前 N 项和的题,四个选项的差别就落在这几个点上:sum有没有初始化、sum声明成int还是double、累加项写1.0/i还是1/i、for里的分隔符是分号还是逗号。逐项排查的方法是按顺序问自己四个问题:变量初始化了吗、类型够宽吗、除法是浮点还是整数、语法结构对吗。四个都对,才是正确答案。

sum未初始化是个隐蔽的坑。int i, sum;之后直接sum = sum + i;,用的是不确定值,结果不可预期。理论考里凡是出现「执行以下程序段,sum 的值是」而sum没初始化的,判定一律往「结果不确定」上靠,不要真去算。

3.3 赋值=与相等==的混淆

if (x = 20)这种写法在语法上合法,在语义上是错的。=是赋值运算符,整个表达式的值是赋值之后x的值,也就是 20;20 非零,条件恒为真,else分支永远进不去,x也被改成了 20。题目问「执行后 x 的值不变」,判定是错——x被改写了。

同类题里还有else if (x = 0)的变体,这时候赋值表达式的值是 0,条件恒为假,这个分支永远进不去,x同样被改成 0。两个方向都要会判:赋非零值恒真,赋零值恒假。

注意:编译器对if (x = 20)通常只给警告不给错误,所以代码能编译、能运行、结果错。理论考抓的就是这个「能跑但不对」的缝隙。

防这类错误有个几乎零成本的习惯:写条件时把常量放左边,if (20 == x)。万一手滑写成if (20 = x),编译器直接报错,因为 20 不是左值。这个写法在团队里叫「尤达条件」,看着别扭,但确实能省掉一类排查成本很高的调试。

3.4 scanf 与 printf 的格式串对齐

格式串和变量类型必须严格对应,这是输入输出部分唯一的硬规则。对应关系如下:

数据类型scanf 格式符printf 格式符常见错法
int%d%d用%f读 int
float%f%f—
double%lf%fscanf 用%f读 double
char%c%c用%d读字符
字符串%s%s—

最容易记混的一格是double:scanf读double必须用%lf,因为要区分float和double的存储宽度;printf输出double用%f就行,因为可变参数在传递时float已经自动提升为double,不需要再区分。这一格不对,输入输出全是垃圾值。

double x; /* 正确:double 配 %lf */ scanf("%lf", &x); printf("%.2f\n", x); /* 输出保留两位小数 */ /* 错误示范一:double 配 %d,读进来的是垃圾,且可能破坏内存 */ scanf("%d", &x); /* 错误示范二:double 配 %f,格式符宽度对不上 */ scanf("%f", &x); /* 错误示范三:变量是 int,格式串却写 %lf */ int year; scanf("%lf", &year);

格式串里除了转换说明,还可以夹普通字符,这些字符在输入时必须原样敲进去。scanf("x=%lf", &x)要求用户输入x=30这样的形式,只敲30是匹配不上的,x拿不到值。这类题判断的关键是:格式串里的非格式字符属于「必须匹配的字面内容」,不是提示文字。

scanf里空格的处理也常考。格式串中的空格表示「跳过任意数量的空白字符」,scanf("%d %lf %d", &a, &b, &c)里两个空格的作用是允许输入时用任意多个空格、制表符或换行分隔。但格式串末尾多写一个空格,会让scanf停下来等更多输入,因为它在等一个非空白字符来结束「跳过空白」这个动作。

printf这边重点是精度和宽度。%.2f表示小数点后保留两位,%4d表示输出至少占 4 个字符宽度、右对齐、不够左边补空格,%6.1f表示总宽度 6、小数一位。这类格式串在算循环执行次数、输出对齐的题里经常配套出现,判断时不需要精确还原输出,只要确认格式符和变量类型对得上。

3.5 字符常量与 ASCII 码

字符常量用一对单引号括起单个字符,'A'、'z'、'0'、'$'都是合法的字符常量。字符型数据的值就是它在 ASCII 字符表里的次序值,所以'A'的值是 65,'a'是 97,'0'是 48。判定题里最爱考的两个反直觉点:

第一,数字字符'1'的值不是数字 1,是 49。'1' - '0'才等于 1,这是把字符转成对应数值的标准写法。

第二,大小写字母的 ASCII 码差 32,且小写比大写大。'a' - 'A' == 32,所以'a'的值比'Z'大。'D' - 'A' == 'd' - 'a'这个表达式为真,因为两边都等于 3,大小写偏移量相同。

排列规律要记牢三组:'0'到'9'升序连续,'A'到'Z'升序连续,'a'到'z'升序连续。但三组之间不连续——'9'后面紧跟的不是'A','Z'后面紧跟的也不是'a',中间夹着其他符号。

字符输入输出用getchar和putchar,不是只能靠printf和scanf。putchar('S')在屏幕上输出的是S,不带引号;引号只是代码里标记「这是字符常量」的语法符号,不参与输出。getchar()从输入流读一个字符,scanf("%c", &ch)也能读,两者在单字符场景下都能用。

4. 流程控制:if-else 配对、switch 穿透与 for 边界

流程控制这部分题的特点是「代码看着顺,结论反着来」。缩进、换行、大括号位置都会骗人,唯一可信的是语法规则。复习时把每条规则还原成一句话,遇到题就拿这句话去套,比凭眼睛扫代码靠谱得多。

4.1 悬空 else 与分支覆盖

else的配对规则只有一句:和之前最近的、还没有配对的if配对。缩进不参与配对,纯粹是给人看的排版。

int a = 2, b = -1, c = 2; if (a < b) if (b < 0) c = 0; else c++; /* 这个 else 配的是内层 if (b < 0),不是外层 */

分析这段:a < b即2 < -1为假,外层if的整个语句体(包含内层if-else)被跳过,c保持 2。如果误以为else配外层,就会算成c加 1 得 3。这类题的正确做法是先在纸上给每个if标号,再连线配对,别用眼睛直读。

分支覆盖的题考的是测试用例设计。规则是:每个分支至少要被一组用例走到,用例数等于需要区分的等价类数量。几个典型数字要记住:

分支结构分支数最少用例数说明
if (grade < 60)单分支22小于 60、大于等于 60
if-else双分支22真假各一
if-else if-else三分支33大于、小于、等于
else-if五区间55小于 0、0、0 到 15、15、大于 15
字符分类三分支36352 字母 + 10 数字 + 1 其他

字符分类那道题容易算错,因为分支只有三个,但字母分支覆盖的是 52 个字符(26 大写 + 26 小写),数字分支覆盖 10 个,其他分支至少 1 个,加起来 63。这里的原则是:用例数按「能区分出不同处理路径的输入类别」算,不是按分支语句的条数算。

还有一类嵌套题要注意:if或else后面跟的分号会截断分支体。

int x = -5, y = 0; if (x >= 0); /* 分号让 if 的分支体变成空语句 */ if (x > 0) y = 1; else y = -1; /* 这个 else 配的是内层 if,与外层无关 */ /* 结果:x = -5,内层 if (x > 0) 为假,走 else,y = -1 */

4.2 switch 的三条硬规则与穿透

switch的规则不多,但每条都能单独出题。第一条:case后面必须跟常量表达式,不能是变量,也不能是关系表达式。case choice == 1:不合法,因为choice == 1是一个求值结果,不是常量。第二条:同一个switch里case的值不能重复。第三条:default可以省略,位置可以放在任意地方,不一定在最后。

执行流程分两种,差别全在break上:

写法匹配到 case 之后的行为常见考点
每个 case 带break执行对应语句段后跳出 switch与 if-else 链等价
不带break从匹配位置一直往下执行,穿透到后面所有 case输出结果叠加
default写在中间位置不影响匹配优先级,只有全部 case 不匹配才进判断等价性

不带break的穿透不是 bug,是有意设计的特性,只是大多数场景下不符合预期。看这道题:

int x = 1, a = 0, b = 0; switch (x) { case 0: b++; /* x 是 1,不匹配,跳过 */ case 1: a++; /* 匹配,执行;没有 break,继续往下 */ case 2: a++; b++; /* 穿透进来,继续执行 */ } /* 结果:a = 2, b = 1 */

判断这类题的方法是从匹配点开始往下数,数到switch的右大括号为止,中间遇到的每条语句都算执行。等价性判断题则反过来:把每个switch的完整执行结果(包括default兜底)算出来,逐一比对,只有在所有输入下结果都相同才叫等价。default放在最前面但带break的写法,和放在最后的写法是等价的,因为匹配优先级相同。

break的使用范围也要记清:break可以用在switch和循环里,不是只能配switch;switch里不一定非用break不可,也不一定非用default不可。两条「必须」都是错的。

4.3 for 三表达式的执行顺序与次数计算

for (表达式1; 表达式2; 表达式3) 循环体的执行顺序固定:表达式1 只在进入循环前执行一次;表达式2 在每次循环体执行前求值,为真就执行循环体,为假就结束循环;表达式3 在每次循环体执行完之后执行。

int fahr; double celsius; /* 表达式1:fahr = 91,只执行 1 次 表达式2:fahr <= 100,每次循环前判断,共判断 11 次(第 11 次为假) 表达式3:fahr++,每次循环体后执行,共执行 10 次 */ for (fahr = 91; fahr <= 100; fahr++) celsius = 5.0 * (fahr - 32) / 9.0; printf("%4d%6.1f\n", fahr, celsius);

这段代码的循环次数算法是(100 - 91) / 1 + 1 = 10,也就是(终值 - 初值) / 步长 + 1。三个数字要分清:表达式2 求值 11 次(10 次为真、1 次为假)、循环体执行 10 次、表达式3 执行 10 次。循环结束后fahr是 101,不是 100——fahr++在最后一次循环体后把 100 加到了 101,然后表达式2 判断 101 <= 100 为假,退出。问「循环结束时循环变量的值」的题,答案永远是「越过终值的第一个值」。

循环里还有两个初始化问题,属于同一类错误的两个面:

/* 问题一:sum 未初始化,累加结果不可预期 */ int i, sum; for (i = 1; i <= 10; i++) sum = sum + i; /* 正确写法:int i, sum = 0; */ /* 问题二:阶乘的累乘单位元是 1,不是 0 */ int i; double product; product = 0; /* 错:0 乘任何数都是 0,结果恒为 0 */ for (i = 1; i <= 20; i++) product = product * i; /* 正确写法:product = 1; */

累加的单位元是 0,累乘的单位元是 1。这个「单位元」的概念比死记「加法初始化为 0、乘法初始化为 1」更牢,遇到新的累运算(累除、累乘方)也能自己推。另外,累乘的变量类型要够宽,int存 13 以上的阶乘就会溢出,题目里通常把product声明成double就是在提示这一点。

5. 易漏点:五类最容易翻车的判定

这一章把散在各处的坑集中收一遍。每条按「易漏点 → 为什么漏 → 怎么补」写,补法都是能立刻用上的动作,不是「多练习」这种空话。

5.1 易漏点一:整数除法的取整发生在赋值之前

为什么漏:眼睛看到结果变量是double,就默认整个计算是浮点的,忽略了除法执行的那一刻两个操作数都是int。类型提升只发生在混算时,不发生在赋值时。

怎么补:读到一个除法表达式,先圈出两个操作数,分别看它们声明成什么类型。两个都是整型、又希望得到小数结果,就必须在分子或分母上加.0或强制转换。养成写完除法回头看一眼操作数类型的习惯,比事后 debug 便宜得多。

5.2 易漏点二:格式串和变量类型错位

为什么漏:%d、%f、%lf长得太像,double在scanf里要用%lf、在printf里用%f这个不对称最容易记混。而且类型错了代码照样编译通过,只是结果变成垃圾值。

怎么补:把对应关系压缩成一句口诀——「读 double 带 l,写 double 不带」。scanf是「读」,要%lf;printf是「写」,用%f。另外格式串里的普通字符(比如x=)必须原样输入,这一点在判断题里常被当成「可以省略」。

5.3 易漏点三:条件表达式里混进赋值号

为什么漏:if (x = 20)和if (x == 20)只差一个字符,扫一眼根本分不出来。而且赋值表达式有值,语法合法,编译器只警告不报错。

怎么补:看到if、while、for的条件里出现单个=,立刻标红。判断结论时按「赋值表达式的值决定真假」推:赋非零恒真,赋零恒假,同时被赋值的变量已经被改写。写代码时把常量放左边,让编译器帮你拦。

5.4 易漏点四:switch 的穿透和 default 的位置

为什么漏:case标签看起来像「分支入口」,直觉上以为匹配到哪个就只执行哪个,忘了没有break会一路往下走。default写在最前面时,又容易误以为它有更高的匹配优先级。

怎么补:判穿透类题,从匹配的case开始往下画一条竖线,画到右大括号,线经过的语句全算执行。判default位置类题,记住匹配规则是先比case、全不比才进default,位置只影响代码可读性,不影响逻辑。

5.5 易漏点五:循环结束后的循环变量值

为什么漏:算循环次数时盯着「跑了几轮」,忘了问「跑完之后变量停在哪」。for (i = 1; i <= 10; i++)跑 10 轮,但i结束时是 11,因为i++先执行、条件判断后失败。

怎么补:把三个量分开记——条件判断次数、循环体执行次数、循环变量终值。终值等于「最后一次让条件成立的值再走一步」。题目问「语句①执行了几次」和「循环体执行了几次」时,先确认语句①在循环体内还是循环体外,位置不同答案可能差一次。

6. 复习清单:按判定路径把判断题重走一遍

复习到这一步,知识点已经铺开了,缺的是一条能重复走的路径。理论考的判断题和选择题,绝大多数可以拆成三步判定,按顺序走,比凭印象答稳得多。

步骤看什么典型结论
第一步:语法边界运算符是否完整、声明是否在前、分号位置缺乘号、声明后用、for 后多分号
第二步:类型匹配操作数类型、格式串、赋值目标整数除法、%lf对 double、左值检查
第三步:流程走向else配对、break有无、循环边界就近配对、穿透、终值越界

第一步最容易得分,因为它只要求你认识语法。看到5(fahr-32)/9、x + y = 22、x % 2(x是double)这类写法,先别管它想算什么,只看合不合法。这一步过了再往下。

第二步是失分重灾区。做一道输入输出题,先把变量声明抄在草稿纸左边,格式串抄在右边,一一连线,连不上的地方就是答案。做一道表达式题,把每个操作数的类型标在它头顶上,除法、取模、赋值三类运算逐个核对。

第三步管流程。判断题里出现嵌套if,先给每个if编号再配对;出现switch,先看有没有break;出现for,先把三个表达式各执行几次列出来。这三件事都是机械动作,练几遍就能形成条件反射。

有一条血泪经验值得记:题库里少数题目的标注和语法规则存在出入,比如隐式乘法、格式串类型这类。遇到这种题,考试时按题库记,写代码时按规则来,两套系统分开存,别用其中一个去覆盖另一个。真正的能力是知道哪条是规则、哪条是题库的约定,而不是把两者混成一锅。

从那以后我每次做理论题前,都强制走一遍「先看语法边界、再看类型匹配、最后看流程走向」这三步,草稿纸上必定留三行痕迹,一行都不省。这份文档我按判断题、选择题分好了块,答案和题干放在一起,适合对着这张清单一条条过。系统学的时候当地图用,考前当查漏补缺用,两种用法都不冲突。希望帮到你。

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

返回列表