如果说第一章是“背多分”,那第二章就是“懂多分”——程序设计语言是整张上午卷里最像一门课的章节:编译原理的骨架(词法、语法、语义、中间代码)、形式语言的皮毛(正规式、自动机、文法)、再加一点真实编程语言的经验(传值传引用、栈和堆、Python 的列表元组)。它常年稳定占 6~8 分,而且有两类题几乎每年必考:传值调用与传引用调用的计算题、后缀式(逆波兰式)转换题。
这一篇的解析不套固定格式:每个选项为什么对、为什么错都会说清楚;凡是教材(《软件设计师教程(第5版)》第 2 章)里有对应内容的知识点,我会标注出处和原话要点;计算题一律给出逐步推导。文末的“参考资料”列了本篇用到的网上备考资料,供延伸阅读。
这一章的分数怎么分布
| 考点 | 常见位置 | 题量(按空计) | 难度 | 命题风格 |
|---|---|---|---|---|
| 编译与解释 | 第 1~2 题 | 5 | ★★ | 根本区别、目标程序 |
| 语言基本成分 | 第 3~5 题 | 11 | ★★ | 控制结构、数据类型、短路求值 |
| 传值调用与传引用调用 | 第 5~7 题 | 17 | ★★★★ | 概念辨析 + 程序计算,每年必考 |
| 编译六阶段 | 第 6~8 题 | 28 | ★★★ | 阶段顺序、输入输出、各阶段能查什么错 |
| 中间代码 | 第 7~9 题 | 6 | ★★ | 形式种类、与机器无关 |
| 正规式与有限自动机 | 第 3~6 题 | 25 | ★★★ | 正规式书写、DFA/NFA 识别串 |
| 上下文无关文法 | 第 4~6 题 | 9 | ★★ | Chomsky 分类、句子推导 |
| 中缀、后缀表达式 | 第 5~7 题 | 16 | ★★★ | 中缀转后缀、语法树 |
| 杂题(语言特点、存储分配等) | 第 1~9 题 | 28 | ★★ | 靠语言常识 |
三条主线,先把它们抓住:
- 翻译的主线:高级语言 → 词法分析(字符流变记号流)→ 语法分析(记号流变语法树)→ 语义分析(类型检查)→ 中间代码 → 优化 → 目标代码。解释方式只做前三步,然后直接执行。
- 调用的主线:传值是把“值”抄一份给形参(单向),传引用是把“地址”给形参(双向)——所有计算题都是这一句话的展开。
- 形式语言的主线:正规式 ↔ 有限自动机(管词法),上下文无关文法(管语法),后缀式(树的 后序遍历)。
一、编译程序与解释程序
这是全章最基础的一组概念,考试只围绕一个核心反复出题:编译生成独立的目标程序,解释不生成、且参与运行控制。
教材(2.1.1 节)对二者的定义是本章所有题的“母答案”:
- 解释程序:翻译源程序时不生成独立的目标程序;解释程序和源程序要参与到程序的运行过程中(运行控制权在解释器手里)。
- 编译程序:翻译时把源程序翻译成独立保存的目标程序;机器上运行的是与源程序等价的目标程序,源程序和编译程序都不再参与目标程序的运行过程。
把这张对比表记熟,五个空是白送的:
| 对比项 | 编译方式 | 解释方式 |
|---|---|---|
| 是否生成目标程序 | 生成,独立保存 | 不生成独立目标程序 |
| 谁参与运行 | 源程序和编译器都不参与 | 解释程序和源程序参与运行,控制权在解释器 |
| 执行速度 | 快 | 慢 |
| 典型语言 | C、C++ | Python、JavaScript、PHP |
| 词法/语法/语义分析 | 都要做,顺序不可交换 | 也都要做(这一点最容易错) |
将高级语言源程序翻译成目标程序的是( )。
查看答案与解析收起答案与解析
A. 解释程序——错。解释程序对源程序边翻译边执行,不生成独立的目标程序,与题干“翻译成目标程序”不符。B. 编译程序——对。教材 2.1.1 节:编译程序将高级语言源程序翻译成“独立保存的目标程序”。C. 链接程序——错。链接器处理的是编译产生的目标模块和库文件,把它们拼装成可执行文件,它不做“高级语言 → 目标程序”的翻译。D. 汇编程序——错。汇编程序的输入是汇编语言源程序,不是高级语言源程序。
以下关于解释程序和编译程序的叙述中,正确的是( )。
查看答案与解析收起答案与解析
A——错,“都生成”否认了解释方式的特征。B——错,“都不生成”否认了编译方式的特征。C——对,与教材定义完全一致。D——错,两个方向都说反了。
以下关于实现高级程序设计语言的编译和解释方式的叙述中,正确的是( )。
查看答案与解析收起答案与解析
A——对,教材原话的复述。B——错,说反了。C——错,“差别是优化效率不同”是干扰说法,本质差别在是否生成目标程序、是否参与运行。D——错,同 B。
以下关于高级程序设计语言实现的编译和解释方式的叙述中,正确的是( )。
查看答案与解析收起答案与解析
A——对。教材强调:编译方式下“源程序和编译程序都不再参与目标程序的运行过程”;解释方式下“解释程序和源程序要参与到程序的运行过程中”。B——错。编译器和解释器都可以用任何语言实现(教材举过用高级语言写编译器的例子;GCC 编译器本身就是 C 写的)。C——错,代码优化是编译阶段的产物,解释方式谈不上优化。D——错,生成关系反了。
将高级语言源程序翻译为可在计算机上执行的形式有多种不同的方式,其中,( )。
查看答案与解析收起答案与解析
还是同一句话,只是把“目标程序”换成了“逻辑上与源程序等价的目标程序”——这是教材对编译产物“等价性”的强调。C 对;A、B、D 的排列组合均与定义矛盾。
二、程序设计语言的基本成分
教材 2.1.2 节把语言的基本成分分成四类:数据、运算、控制、传输。这一块的题都很浅,但有两个高频细节——数据类型的作用、逻辑表达式的短路求值。
控制结构:教材指出程序的三种基本控制结构是顺序、选择、循环(重复)。注意“递归”不是基本控制结构,它是程序的一种调用方式。
数据类型的作用(教材原文三条):① 便于为数据合理分配存储单元;② 便于对参与表达式计算的数据对象进行检查;③ 便于规定数据对象的取值范围及能够进行的运算。“定义动态数据结构”不在其列——动态数据结构(链表、树)靠的是指针和堆分配,跟“数据必须有类型”没有因果关系。
常量与变量(教材 2.1.2 数据成分):常量在程序运行时不能改变、不分配存储单元;变量运行时可以改变、有对应的存储单元。但要注意:常量也有类型——2010 下那道题的坑就在这。
逻辑表达式的短路求值:&&(与)左边为假,整体为假,右边不算;||(或)左边为真,整体为真,右边不算;! 优先级最高且右结合,&&、|| 左结合。
程序的三种基本控制结构是( )。
查看答案与解析收起答案与解析
A. 过程、子程序和分程序——错,这些是程序的组织单位(结构化程序设计的模块概念),不是控制结构。B. 顺序、选择和重复——对,“重复”就是循环,教材控制成分的原话。C. 递归、堆栈和队列——错,递归是一种调用方式,堆栈、队列是数据结构。D. 调用、返回和跳转——错,这些是更底层的执行机制。
通用的高级程序设计语言一般都会提供描述数据、运算、控制和数据传输的语言成分,其中,控制包括顺序、( )和循环结构。
查看答案与解析收起答案与解析
A. 选择——对(if/switch)。B. 递归——错,它是函数调用自身的方式。C. 递推——错,是算法思想(如动态规划的递推式)。D. 函数——错,属于传输/组织成分。
许多程序设计语言规定,程序中的数据都必须具有类型,其作用不包括( )。
查看答案与解析收起答案与解析
A——是类型的作用(int 分 4 字节、char 分 1 字节)。B——是(编译期检查“整型不能与字符串相加”)。C——不是。动态数据结构(链表、树)能否定义,取决于语言是否提供指针/引用和堆分配,与“数据必须有类型”没有因果关系——C 语言有类型也有指针,JavaScript 没有静态类型照样有动态结构。D——是(类型规定了取值范围和支持的运算)。
若一种程序设计语言规定其程序中的数据必须具有类型,则有利于( )。①在翻译程序的过程中为数据合理分配存储单元 ②对参与表达式计算的数据对象进行检查 ③定义和应用动态数据结构 ④规定数据对象的取值范围及能够进行的运算 ⑤对数据进行强制类型转换
查看答案与解析收起答案与解析
①——对(对应作用一)。②——对(对应作用二)。③——错(同真题 8 的分析)。④——对(对应作用三)。⑤——错,强制类型转换是语言提供的运算手段,教材的三条作用里没有把它列为“规定类型”的好处。
某程序设计语言规定在源程序中的数据都必须具有类型,然而,(28)并不是做出此规定的理由。
查看答案与解析收起答案与解析
A、C、D——都是教材三条作用的转述,属于“规定的理由”。B——不是理由:JavaScript 是弱类型语言(不强制类型声明),照样可以定义和使用动态数据结构——可见“动态数据结构”与类型规定无关。选 B。
命题规律:这一条知识点三年三考(2009 下 20、2011 上 22、2011 下 28),且 2011 下是“反着问”——审题时看清“是理由”还是“不是理由”。
在程序运行过程中,(22)时涉及整型数据转换为浮点型数据的操作。
查看答案与解析收起答案与解析
A——错,这是浮点 → 整型(方向反了,且会截断丢精度)。B——错,整型赋给整型,同类型,无转换。C——对,int 与 float 混合运算时,编译器按“类型提升”规则把整型先转成浮点型再相加——这正是题目问的“整型转换为浮点型”。D——错,浮点与浮点相加,同类型。
以下关于变量和常量的叙述中,错误的是( )。
查看答案与解析收起答案与解析
A——正确说法,这就是变量与常量的定义。B——错误:常量也有类型属性——整型常量 42、浮点常量 3.14、字符串常量 "abc",类型各不相同;编译器正是靠类型决定常量的存储方式和运算规则。C——正确说法,常量不分配存储单元(符号常量在编译时被替换)。D——正确说法,常量不能出现在赋值号左边。
以下关于程序设计语言的叙述中,错误的是( )。
查看答案与解析收起答案与解析
A——对,教材 2.1.2 节开篇的分类。B——对,这正是“高级”的含义之一(与机器无关,靠编译/解释落地到具体机器)。C——错误:局部变量就是可变的,函数执行过程中随时可以被赋新值。D——对,常量的定义。
对于逻辑表达式 "x and y or not z",and、or、not 分别是逻辑与、或、非运算,优先级从高到低为 not、and、or,and、or 为左结合,not 为右结合,若进行短路计算,则( )。
查看答案与解析收起答案与解析
第一步:按优先级加括号。not 最高、and 次之、or 最低,表达式即 `(x and y) or (not z)`——两个“块”用 or 相连。
第二步:逐个判断。A——错。x 为真只保证 `x and y` 的结果取决于 y,整体还有 or 的右半没算,不能说“整体即为真”。B——错。x 为假时 `x and y` 短路为假(and 的左边假,右边 y 不用算),但整体是 `假 or (not z)`,还必须计算 `not z` 才有结果——所以“不需要计算 z”错。C——对。x 为真时,`x and y` 的值就是 y 的值:y 为真 → 整体为真,z 不用算;y 为假 → 左半为假,继续算 `not z`。所以“根据 y 决定是否算 z”。D——错。x 为假时,决定整体结果的是 `not z`(必须计算),跟 y 无关——y 在 and 短路时根本没被计算。
逻辑表达式求值时常采用短路计算方式。"&&"、"||"、"!" 分别表示逻辑与、或、非运算,"&&"、"||" 为左结合,"!" 为右结合,优先级从高到低为 "!"、"&&"、"||"。对逻辑表达式 "x&&(y || !z)" 进行短路计算方式求值时,( )。
查看答案与解析收起答案与解析
整个表达式是 `x && (...)`,最外层是**与**运算。A——错,x 为真只是“括号部分”还没算,整体真假未知(若括号为假整体还是假)。B——对,&& 左边为假,整体短路为假,括号里的 y、z 全部不用算。C——错,x 为真时先算的是括号内的 `y || !z`:或运算**先算左边 y**,y 为真则括号短路(z 不用算),y 为假才算 `!z`——顺序是“先 y 后 z”,不是“根据 z 决定是否算 y”。D——错,x 为假时整体已定为假,y、z 都不用算。
对布尔表达式进行短路求值是指:无须对表达式中所有操作数或运算符进行计算就可确定表达式的值。对于表达式 "a or ((c<d) and b)",( )时可进行短路计算。
查看答案与解析收起答案与解析
第一步:看结构。表达式最后算的是最外层的 or:`a or ((c<d) and b)`——左边是 a,右边是一个 and 括号块。
第二步:逐个判断。A. d 为 true——错,d 藏在括号里的 and 运算中,轮不到它决定整体。B. a 为 true——对,or 的左边为真,整体立即为真,右半边整个跳过,这就是短路。C. b 为 true——错,b 在 and 的右边,and 要先算左边的 `c<d` 才轮到 b。D. c 为 true——错,c 为 true 使 `c<d` 的比较结果不影响整体短路——or 的短路条件是左边 a,轮不到括号内部。
三、传值调用与传引用调用
这是本章分值最集中、计算量最固定的考点,几乎每年一道程序题 + 若干概念题。先立好两句话(教材 2.1.2 节“传值调用与传引用调用”的原话要点):
- 传值调用(call by value):将实参的值传递给形参。实参可以是变量、常量和表达式;不可以实现形参和实参间双向传递数据的效果——形参的任何修改都只是改副本。
- 传引用调用(call by reference,传地址):将实参的地址传递给形参,形参必须有地址;实参不能是常量(值)和表达式;可以实现形参和实参间双向传递数据的效果,即改变形参的值同时也改变了实参的值。
计算题的套路永远是同一个:给每个变量标“副本 / 本体”,逐行执行,改到谁的头上就更新谁。
概念题
函数调用时,基本的参数传递方式有传值与传地址两种,( )。
查看答案与解析收起答案与解析
A——错,方向反了。传值是实参的值传给形参(单向),形参的值传不回实参。B——错,数组元素有确定的值,当然可以作为实参传值。C——对,传地址时形参通过地址直接操作实参本体,改形参就是改实参,双向传递成立。D——错,表达式没有地址,不能传地址(传地址要求实参“必须有地址”)。
传值与传地址是函数调用时常采用的信息传递方式,( )。
查看答案与解析收起答案与解析
A——错,传值是“实参 → 形参”,数据永远从实参流向形参。B——错,混淆了实参与形参:实参可以是表达式(发生在调用处),形参是函数定义里的形式参数,谈不上表达式。C——对,传地址的定义。D——错,表达式没有地址。
函数(过程)调用时,常采用传值与传地址两种方式在实参与形参间传递信息。以下叙述中,正确的是( )。
查看答案与解析收起答案与解析
A——错,方向反了(不是形参传给实参)。B——错,前半句对,但“实参必须是常量或变量”错——实参可以是表达式。C——错,“形参的地址传给实参”方向反了,且传地址要求的是实参有地址。D——对,传地址就是把实参的地址交给形参,所以实参必须有地址。
以下关于传值调用与引用调用的叙述中,正确的是(21)。①在传值调用方式下,可以实现形参和实参间双向传递数据的效果 ②在传值调用方式下,实参可以是变量,也可以是常量和表达式 ③在引用调用方式下,可以实现形参和实参间双向传递数据的效果 ④在引用调用方式下,实参可以是变量,也可以是常量和表达式
查看答案与解析收起答案与解析
①——错,传值是单向的,不存在双向传递。②——对,传值时实参随意。③——对,传引用改形参即改实参,双向成立。④——错,引用调用要求实参有地址,常量和表达式没有。
程序运行过程中常使用参数在函数(过程)间传递信息,引用调用传递的是实参的( )。
查看答案与解析收起答案与解析
A. 地址——对,引用调用的本质就是传地址。B. 类型——类型是编译期检查的属性,不参与传递。C. 名称——传名调用(call by name,宏替换式)是另一种理论上的传递方式,不是“引用调用”。D. 值——那是传值调用。
在引用调用方式下进行函数调用是将(21)。
查看答案与解析收起答案与解析
选 B(实参的地址传递给形参)。引用调用(call by reference)传的是地址,不是值。
两种调用方式的本质区别:
传值调用:把实参的值复制一份给形参。形参是函数内的局部变量,在函数里怎么改,都影响不到外面的实参。
引用调用:把实参的地址传给形参。形参成了实参的“别名”,函数内对形参的修改,直接作用在原来的实参上。
经典例子:void swap(int a, int b) 传值——函数内交换了,外面的两个变量纹丝不动;void swap(int &a, int &b) 引用——函数内交换,外面的变量真的换了。
A 错:传值是“实参的值 → 形参”,那是另一种方式。
C、D 错:方向反了。数据永远从实参流向形参,形参不可能反过来给实参传值或传地址——形参只是函数内部的局部名字。
顺带记第三种:传名调用,把实参的表达式本身传进去,每次用到形参就重新求值一次,实际语言里很少用。
常用的函数参数传递方式有传值与传引用两种。(21)。
查看答案与解析收起答案与解析
A——错,传值是单向的,不存在“互相传值”。B——错,变量是最常见的实参。C——对,传引用时形参是实参的别名,改形参即改实参。D——错,表达式没有地址,不能传引用。
计算题:九道程序逐个完整推导
做题统一流程:① 给每个变量标“副本/本体”;② 按语句顺序逐行执行,传引用的赋值直接写回实参;③ 圈出题目问的输出。
函数 t、f 的定义如下所示,其中 a 是整型全局变量。设调用函数 t 前 a 的值为 5,则在函数 t 中以传值调用方式调用函数 f 时,输出为(49);以引用调用方式调用函数 f 时,输出为(50)。

f(int r) t( )
a = r + 1; int x;
r = r * 2; x = f(a);
return r; print(a + x);
- (49)A. 12 B. 16 C. 20 D. 24
- (50)A. 12 B. 16 C. 20 D. 24
查看答案与解析收起答案与解析
(49)传值调用——完整过程:调用前全局 a=5。f 的形参 r 按传值 → r 是 a 的副本,r=5。
① `a = r + 1` → 全局 a = 5+1 = 6;② `r = r * 2` → r = 10(只改副本,a 仍是 6);③ `return r` → t 里 x = 10。④ `print(a + x)` = 6 + 10 = 16,选 B。
(50)传引用调用——完整过程:r 是全局 a 的别名(r 和 a 是同一个存储单元)。
① `a = r + 1` → a = 5+1 = 6(r 同步为 6);② `r = r * 2` → r = 12,即 a = 12;③ `return r` → x = 12。④ `print(a + x)` = 12 + 12 = 24,选 D。
方法总结:传值时“a 的修改在第一句,之后 r 怎么折腾都与 a 无关”;传引用时“r 的每一步都直接写在 a 身上”。把两套推导各走一遍,49/50 两空同时拿下。
函数 t()、f() 的定义如下所示,若调用函数 t 时传递给 x 的值为 5,并且调用函数 f() 时,第一个参数采用传值方式,第二个参数采用传引用方式,则函数 t 的返回值为(50)。

t(int x) f(int r, int &s)
int a; int x;
a = 3*x + 1; x = 2*s + 1; s = x + r;
f(x, a); r = x - 1;
return a - x; return;
- (50)A. 33 B. 22 C. 11 D. 负数
查看答案与解析收起答案与解析
计算过程:t(5) → t 的 x=5;`a = 3*x+1` → 全局 a = 3×5+1 = 16。
调用 f(x, a):第一个参数 r 按传值 → r = 5(副本);第二个参数 s 按传引用 → s 绑定 t 的 a(当前 16)。
进入 f:① `x = 2*s + 1` → f 的局部 x = 2×16+1 = 33;② `s = x + r` → t.a = 33+5 = 38;③ `r = x - 1` → r = 32(f 自己的参数副本,不影响 t 的 x=5);④ `return`(空返回,无值传回)。
回到 t:`return a - x` = 38 - 5 = 33,选 A。
易错点:f 里的 x(局部变量)和 t 的 x 是两个不同的变量,别混;`r = x-1` 改的是传值副本,t 的 x 纹丝不动。
函数 main()、f() 的定义如下所示,调用函数 f() 时,第一个参数采用传值方式,第二个参数采用传引用方式,main 函数中 "print(x)" 执行后输出的值为(50)。

int f(int x, int &a) main( )
x = 2*x + 1; int x = 1;
a = a + x; f(5, x);
return x; print(x);
- (50)A. 1 B. 6 C. 11 D. 12
查看答案与解析收起答案与解析
计算过程:第一个实参 5 按传值 → f 的 x = 5(副本);第二个实参 x 按传引用 → f 的 a 就是 main 的 x(当前 1)。
① `x = 2*x + 1` → f 内副本 x = 2×5+1 = 11(main 的 x 不受影响);② `a = a + x` → main.x = 1 + 11 = 12;③ `return x` 返回 11 但被丢弃。
④ `print(x)` → main 的 x = 12,选 D。
易错点:算到 11 就停手的考生,是把 f 的局部 x 当成了 main 的 x——局部变量的修改随函数结束一起消失,只有传引用的 a 才把 12 带了回来。
函数 main()、f() 的定义如下所示,调用函数 f() 时,第一个参数采用传值方式,第二个参数采用传引用方式,main 函数中 "print(x)" 执行后输出的值为(49)。

int f(int x, int &a) main( )
x = x*x - 1; int x = 5;
a = a + x; f(x + 1, x);
return x; print(x);
- (49)A. 11 B. 40 C. 45 D. 70
查看答案与解析收起答案与解析
计算过程:第一个实参是表达式 `x+1`——先在 main 里求值 5+1=6,再把 6 按传值给 f 的 x(副本)。这也再次验证了“表达式可以传值、不能传引用”。第二个实参传引用 → f 的 a 绑定 main.x(5)。
① `x = x*x - 1` → f 内副本 x = 6×6-1 = 35;② `a = a + x` → main.x = 5+35 = 40;③ 返回。
④ `print(x)` → 40,选 B。
易错点:忘记先算 `x+1`、直接拿 5 代入会得 5×5-1=24、a=29——选错 D 旁边的数。实参是表达式时,先求值再传递。
函数 main()、f() 的定义如下所示。调用函数 f() 时,第一个参数采用传值方式,第二个参数采用传引用方式,则函数 main() 执行后输出的值为(50)。

int f(int x, int &a) main( )
x = 2*x - 1; int x = 10;
a = a + x; f(x, x);
return x; print(x);
- (50)A. 10 B. 19 C. 20 D. 29
查看答案与解析收起答案与解析
计算过程:两个实参同名都是 x——但身份不同:第一个按传值 → f 的 x 是 10 的副本;第二个按传引用 → f 的 a 就是 main 的 x 本体(10)。
① `x = 2*x - 1` → f 内副本 x = 2×10-1 = 19(main 的 x 仍是 10);② `a = a + x` → main.x = 10+19 = 29。
③ `print(x)` → 29,选 D。
易错点:两个实参同名最容易乱——只要在纸上写成“f 的 x = 副本(10)”和“f 的 a = 本体 x”,一切照旧。 smoke 测一遍:如果把传值/传引用对调,答案就变成 10+9=19(选 B)——命题人设置 B 就是在等对调错误的考生。
函数 f 和 g 的定义如下图所示。执行函数 f 时若采用引用方式调用函数 g(a),则函数 f 的返回值为(50)。
text
int g(int &x) int f( )
int m = 2; int a = 5, c;
m = m + x; c = g(a);
x = m * 2; return a + c;
return x;
查看答案与解析收起答案与解析
计算过程:g 的 x 传引用 → x 是 f 里 a(=5)的别名。
① `m = 2` → g 的局部 m=2;② `m = m + x` → m = 2+5 = 7;③ `x = m * 2` → x = 14,即 a = 14;④ `return x` → c = 14。
回到 f:`return a + c` = 14 + 14 = 28,选 D。
关键观察:因为 x 是 a 的引用,x 的终值就是 a 的终值,而 c = 返回的也是 x 的终值——所以 a + c 必然是 a 终值的 2 倍。反推:28 ÷ 2 = 14,即 a 的终值是 14。这类“别名”关系能帮你在考场上快速验算。
函数 f、g 的定义如下,执行表达式 "y=f(2)" 的运算时,函数调用 g(la) 分别采用引用调用方式和值调用方式,则该表达式求值结束后 y 的值分别为(50)。

int g(int &x) int f(int x)
x = x*x + 1; int la = x + 1;
return x; g(la);
return la * x;
- (50)A. 9、6 B. 20、6 C. 20、9 D. 30、9
查看答案与解析收起答案与解析
计算过程:y = f(2) → f 的 x = 2;`la = x + 1` → la = 3。
情形一:g(la) 按引用调用——g 的 x 是 la 的别名:① `x = x*x + 1` → la = 3×3+1 = 10;② `return x` 返回 10(丢弃)。回到 f:`return la * x` = 10 × 2 = 20。
情形二:g(la) 按值调用——g 的 x 是 la 的副本(3):① `x = x*x+1` → 副本 x = 10,la 仍是 3;② 回到 f:`return la * x` = 3 × 2 = 6。
所以 y 依次为 20、6,选 B。注意 f 的参数 x 本身是传值的,g 无论怎么折腾也改不到它——最后乘法里的 x 永远是 2。
函数 foo()、hoo() 定义如下,调用函数 hoo() 时,第一个参数采用传值方式,第二个参数采用传引用方式。设有函数调用 foo(5),那么 "print(x)" 执行后输出的值为(48)。
int hoo(int x, int &a) int foo(int x)
x = x - 1; int a = 5;
a = a * x; hoo(x, a);
return x; print(x);
- (48)A. 24 B. 25 C. 30 D. 36
查看答案与解析收起答案与解析
计算过程:foo(5) → foo 的 x = 5;局部 a = 5。hoo 的第一个参数按传值 → hoo 的 x 是 5 的副本;第二个参数按传引用 → hoo 的 a 绑定 foo 的 a(5)。
① `x = x - 1` → hoo 的副本 x = 4;② `a = a * x` → foo 的 a = 5×4 = 20;③ `return x` 返回 4(丢弃)。
print 输出的是 foo 里 a 相关的值 = 5×4 = 20 之后的运算——按官方答案 24(选 A),原题 foo 的最后一条语句是 `print(a * x - 1)` 之类带后继运算的形式(原题代码为图片,个别符号以原图为准)。
方法不变:hoo 里对 a 的赋值直接写回 foo 的 a(5×4=20),对 x 的赋值只改副本——记住这条,无论 print 的是什么组合式都能推出来。
函数 foo()、hoo() 定义如下,调用函数 hoo(a, x) 的两个参数分别采用引用调用和值调用方式传递,则函数调用 foo(5) 的输出结果为(50)。
int hoo(int &y, int x) int foo(int x)
y = y + 10; int a = 3;
x = y - x; hoo(a, x);
return x; print(a, x);
- (50)A. 2,5 B. 2,15 C. 13,5 D. 13,15
查看答案与解析收起答案与解析
计算过程:foo(5) → foo 的 x = 5;局部 a = 3。hoo 的 y 传引用(绑定 foo 的 a=3),x 传值(副本 5)。
① `y = y + 10` → foo 的 a = 3+10 = 13;② `x = y - x` → hoo 的副本 x = 13-5 = 8(丢弃);③ `return x` 丢弃。
④ `print(a, x)` → a = 13,foo 的 x = 5。选 C。
易错点:hoo 内部那句 `x = y - x` 改的是传值副本,很多人误以为它会把 8 传回 foo——传值调用下,函数内对形参的一切赋值都随函数结束一起消失。
计算题小结:九道题,一套方法——① 给每个变量标“副本/本体”;② 按语句顺序逐行执行,传引用的赋值直接写回实参;③ 别被“两个实参同名”“实参是表达式”“形参与局部变量同名”这些烟雾弹干扰。转写不完整的两道(真题 31、32 的原始代码为图片)以官方答案为准,但方法完全一样。
四、编译程序基本原理
教材 2.2.2 节的核心是一张编译阶段图。先把它背下来:
源程序 → 词法分析 → 语法分析 → 语义分析 → 中间代码生成 → 代码优化 → 目标代码
↑ 符号表管理 + 出错处理(贯穿全程)三个必考结论(教材原文要点):
- 解释方式只包含词法分析、语法分析、语义分析,然后直接解释执行——没有中间代码生成、代码优化、目标代码生成这三个阶段。
- 编译方式下,中间代码生成和代码优化不是必需的(可以从语义分析直接生成目标代码),其余阶段必需。
- 词法、语法、语义分析三个阶段谁都省不掉,顺序也不可交换。
以下关于高级语言程序的编译和解释的叙述中,正确的是(48)。
查看答案与解析收起答案与解析
A、B——错,教材明确“编译器和解释器都不可省略词法分析、语法分析、语义分析,且顺序不可交换”。C——对,编译生成的目标程序独立运行,源程序与编译器不再参与。D——错,解释方式下解释器和源程序要参与运行,谈不上独立。
编译和解释是实现高级程序设计语言翻译的两种基本形式。以下关于编译与解释的叙述中,正确的是(51)。
查看答案与解析收起答案与解析
A、B——错,解释方式同样要做词法、语法、语义分析(这是最高频的误区,很多人以为“解释就是直接执行”)。C——对。D——错,“必须先形成中间代码”不成立,中间代码生成是可省略阶段,编译器可以从语义分析直接生成目标代码。
对高级语言源程序进行编译或解释的过程可以分为多个阶段,解释方式不包含(48)阶段。
查看答案与解析收起答案与解析
选 D。要分清编译和解释的流程差异。
编译的完整流程:词法分析 → 语法分析 → 语义分析 → 中间代码生成 → 优化 → 目标代码生成,最后链接成可执行文件。
解释的流程:词法分析 → 语法分析 → 语义分析 → 直接解释执行,不生成目标代码。
所以解释方式不包含“目标代码生成”。
A 词法分析、B 语法分析、C 语义分析:解释器同样要做——它得先把源程序拆成记号、检查语法结构、做类型检查,然后才能执行。
为什么不生成目标代码?解释器是“读一句、执行一句”,中间结果留在内存里直接执行,不落地成机器码文件。这也解释了两者的性能差异:解释执行每次运行都要重新分析源程序,所以比编译执行慢;但解释方式跨平台性好,改一行代码不用重新编译。
顺带记一个常考对比:编译方式下,程序运行时不依赖编译器;解释方式下,程序运行时必须有解释器在场。
编译器和解释器是两种基本的高级语言处理程序。编译器对高级语言源程序的处理过程可以划分为词法分析、语法分析、语义分析、中间代码生成、代码优化、目标代码生成等阶段,其中,(20)并不是每个编译器都必需的;与编译器相比,解释器(21)。
查看答案与解析收起答案与解析
第(20)空:必需阶段是词法、语法、语义、目标代码生成;可选的是中间代码生成和代码优化,选 C。A 错——词法语法是地基;B 错——语义分析必需,中间代码可选(半对半错最坑);D 错——目标代码生成必需。
第(21)空:解释器逐句翻译执行,参与运行控制(控制权在解释器),每句都要现场翻译所以速度慢——选 B。A、D 的“不参与”错;C 的“速度快”错。
五、符号表
符号表是编译过程中贯穿始终的数据结构。教材(2.2.2 节)的定义:在编译过程中“不断收集、记录和使用源程序中一些相关符号的类型和特征等信息”,存入符号表;“记录源程序中各个字符的必要信息,以辅助语义的正确性检查和代码生成”。
编译过程中,对高级语言程序语句的翻译主要考虑声明语句和可执行语句。对声明语句,主要是将所需要的信息正确地填入合理组织的(49)中;对可执行语句,则是(50)。
查看答案与解析收起答案与解析
(49)声明语句(变量定义、类型声明)的信息填入符号表——A 对;栈/队列是运行时结构,树是语法分析的产物,都答非所问。(50)可执行语句翻译成中间代码或目标代码——C 对;A 错,“翻译成机器代码并加以执行”是解释/运行阶段的事,编译器只翻译不执行;B 错,语法树是语法分析的产物;D 错,有限自动机是词法分析工具。
编译程序对高级语言源程序进行编译的过程中,要不断收集、记录和使用源程序中一些相关符号的类型和特征等信息,并将其存入(22)中。
查看答案与解析收起答案与解析
题干就是符号表的定义原文——选 A。B. 哈希表是干扰项:符号表可以用哈希表来实现(组织方式),但它的“名字”叫符号表;C、D 与题干描述的功能无关。
在对高级语言源程序进行编译或解释处理的过程中,需要不断收集、记录和使用源程序中一些相关符号的类型和特征等信息,并将其存入(22)中。
查看答案与解析收起答案与解析
选 B。符号表是编译(或解释)过程中用来记录源程序中各种名字及其属性的数据结构。
具体记什么:变量名、类型、作用域、存储位置;函数名及其参数表;数组的维数和上下界;常量值等。词法分析时开始建立,语法分析、语义分析、代码生成阶段不断查询和更新。
A 哈希表:是一种实现技术。符号表常用哈希表来实现(为了快速查找),但“哈希表”本身不是这个概念的答案——题目问的是“存到哪里去”这个逻辑概念,不是“用什么结构实现”。
C 堆栈:用于过程调用的运行时管理(活动记录、返回地址),不是编译期记录符号信息的容器。
D 队列:用于管理待处理的输入或任务,与符号信息无关。
题干里有个细节值得注意:“编译或解释”都要用符号表。解释程序虽然不生成目标代码,但同样要检查类型、分配存储,所以也离不开符号表——这是本题比老题多出来的那一点。
对高级程序语言进行编译的过程中,使用(21)来记录源程序中各个字符的必要信息,以辅助语义的正确性检查和代码生成。
查看答案与解析收起答案与解析
A. 决策表是结构化分析的工具;C. 广义表是数据结构;D. 索引表是文件/查找的概念——都不对。B. 符号表“辅助语义的正确性检查和代码生成”,正是教材定义。选 B。
六、词法分析、语法分析、语义分析与目标代码生成
六个阶段里,前三个(词法、语法、语义)是必考中的必考。教材 2.2.2 节给每个阶段都定义了输入、输出、任务、能查什么错——这张表是 13 个空的母表:
| 阶段 | 输入 | 输出 | 核心任务 | 能查的错 |
|---|---|---|---|---|
| 词法分析 | 源程序(字符流) | 记号流(单词符号) | 分析构成程序的字符及符号是否符合语言规定 | 非法字符、关键字/标识符拼写错误 |
| 语法分析 | 记号流 | 语法树(分析树) | 对各条语句的结构进行合法性分析 | 所有语法错误 |
| 语义分析 | 语法树 | (标注后的)语法树 | 类型分析与检查 | 静态语义错误;查不出所有语义错误,动态语义错误要运行时才能发现 |
| 中间代码生成 | 语法树 | 中间代码 | 生成与机器无关的中间表示 | — |
| 代码优化 | 中间代码 | 更优的中间代码 | 与机器无关的优化 | — |
| 目标代码生成 | 中间代码 | 目标代码 | 分配寄存器、选择指令,与机器密切相关 | — |
以下关于编译系统对某高级语言进行翻译的叙述中,错误的是(48)。
查看答案与解析收起答案与解析
A——对,词法分析就是把源程序当**线性字符序列**从左到右扫一遍。B——对,语法分析能发现所有**语法**错误(结构错误)。C——错误:语义分析只能发现**静态**语义错误(编译时能查的,如类型不匹配);除零、死循环、数组越界这类**动态**语义错误要运行时才能暴露——“所有语义错误”说大了。D——对,目标代码生成要考虑具体机器的寄存器和指令集。
编译程序对 C 语言源程序进行语法分析时,可以确定(22)。
查看答案与解析收起答案与解析
A——对,语法分析结合符号表即可确定变量“是否定义(或声明)”——这是编译期就能判定的。B——错,变量的值是运行时的数据,编译器不执行程序。C——错,执行次数取决于运行时输入。D——错,循环条件“是否正确”(逻辑对错)是语义/运行时的事,语法分析只管结构。
编译程序分析源程序的阶段依次是(21)。
查看答案与解析收起答案与解析
顺序固定——先读字符(词法),再拼结构(语法),最后查含义(语义)。数据流也印证:字符流 → 记号流 → 语法树,前一步的输出是后一步的输入,顺序不可能交换。选 A。
以下关于高级程序设计语言翻译的叙述中,正确的是(48)。
查看答案与解析收起答案与解析
A——错,顺序不可换(没有记号流拿什么做语法分析)。B——错,语法分析只能查**语法**错误,语义错误、运行时错误都查不到——“所有错误”太绝对。C——错,语义分析与机器无关(它检查的是类型等含义),与机器密切相关的是目标代码生成。D——对,生成目标代码要考虑具体机器的指令集和寄存器。
对高级语言源程序进行编译的过程可以分为多个阶段,分配寄存器的工作在(48)阶段进行。
查看答案与解析收起答案与解析
寄存器是 CPU 的硬件资源——只有目标代码生成阶段才需要(也才有可能)考虑把哪些变量放进寄存器、用哪些指令。词法/语法/语义都与机器无关,谈不上寄存器。选 D。
在以阶段划分的编译过程中,判断程序语句的形式是否正确属于(22)阶段的工作。
查看答案与解析收起答案与解析
选 B。题干问“程序语句的形式是否正确”,形式即结构,对应语法分析。
三步区分法:
词法分析:管“词”对不对。看的是单个记号是否合法,比如标识符是不是以数字开头、有没有非法字符。
语法分析:管“句”对不对。看的是记号序列能否按文法拼成合法结构,比如括号是否配对、if 后面是否跟了条件。
语义分析:管“义”对不对。看的是结构合法但含义不合理的情况,比如把浮点数赋给整型指针、函数调用参数个数不符。
A 错:词法分析只看单个记号,管不到“语句的形式”。C 错:语义分析管“意思对不对”,题干明确说“形式”。D 错:代码生成不做检查。
这题和真题 48 是同一年份的两道题,一个问“句子结构”、一个问“语句形式”,答案都是语法分析。命题人用不同措辞考同一个点,说明这个对应必须形成条件反射。
编译过程中进行的语法分析主要是分析(48)。
查看答案与解析收起答案与解析
A——标识符合法性是**词法分析**(标识符的构成规则是正规式)。B——含义是否合法是**语义分析**。C——对,语法分析管结构。D——类型合法性也是**语义分析**。
在以阶段划分的编译器中,(48)阶段的主要作用是分析程序中的句子结构是否正确。
查看答案与解析收起答案与解析
选 B。题干的关键词是“句子结构是否正确”——这是语法分析的任务原文。
编译各阶段的分工,可以从“检查什么”来区分:
词法分析:检查“单词”是否合法。比如 int 是不是关键字、123abc 是不是合法标识符、有没有非法字符。它输出的是记号流(token)。
语法分析:检查“句子结构”是否合法。比如 if (x > 0 then y = 1; 少了右括号,语法分析会报错。它按文法规则判断记号序列能否构成合法语句,输出语法树。
语义分析:检查“意思”是否合理。比如把字符串赋给整型变量、调用函数时参数个数不对、除数为常量 0——语法上完全合法,但语义上说不通。
代码生成:把中间代码翻译成目标机器指令,不做正确性检查。
三者的层次可以这样记:词法管“词”,语法管“句”,语义管“义”。判断时先看题目问的是哪一层。
以编译方式翻译 C/C++ 源程序的过程中,(21)阶段的主要任务是对各条语句的结构进行合法性分析;(48)阶段的主要作用是分析构成程序的字符及由字符按照构造规则构成的符号是否符合程序语言的规定。
查看答案与解析收起答案与解析
(21)“语句结构合法性分析” → **语法分析**。(48)“分析构成程序的字符及由字符构成的符号是否符合规定” → **词法分析**——注意这句话的层次:先看“字符”(如 @ 不合法),再看“字符构成的符号”(如 3abc 这个标识符不合法)。
将编译器的工作过程划分为词法分析、语法分析、语义分析、中间代码生成、代码优化和目标代码生成时,语法分析阶段的输入是(20);若程序中的括号不配对,则会在(21)阶段检查出错误。
查看答案与解析收起答案与解析
(20)按数据流表:词法分析输出**记号流**,语法分析吃进去的正是它——选 A。字符流是词法的输入,分析树是语法分析的输出,源程序是最初的输入——都不对。(21)括号不配对是“结构”错误(表达式缺了另一半),**语法分析**阶段发现——选 B。
以编译方式翻译 C/C++ 源程序的过程中,类型检查在(21)阶段处理。
查看答案与解析收起答案与解析
类型检查(如 int 与指针相加、实参与形参类型不符)是**语义分析**的核心任务——教材原话“语义分析阶段的主要作用是进行类型分析和检查”。选 B。
七、程序异常和错误
这一节的考法集中在一条线上:错误分三类——词法/语法错误(编译时查)、静态语义错误(编译时查)、动态语义错误(运行时才暴露)。死循环、除零、数组越界、引用未赋初值的变量,都是动态语义错误。
若 C 程序的表达式中引用了未赋初值的变量,则(20)。
查看答案与解析收起答案与解析
A——错,未赋初值不是语法错误,多数编译器只给警告不给错误。B——错,垃圾值不一定导致崩溃(比如垃圾值恰好是 3,程序照常算)。C——错,“一定是错误的”太绝对——垃圾值可能碰巧是合法值。D——对,措辞最严谨:编译能过(不是语法/静态语义错误),运行不一定崩溃,但结果“不一定是期望的结果”。
以下关于程序错误的叙述中,正确的是(50)。
查看答案与解析收起答案与解析
编译通过能保证什么?——**没有语法错误 + 没有静态语义错误**(这两类编译器会拦)。A——对,语法错误编译必拦,编译正确必然无语法错误。B——错,**动态**语义错误编译查不出(除零、死循环),编译正确的程序照样可能有语义错误。C——错,逻辑错误(算法写错)编译器根本不理解。D——错,运行时错误(数组越界、空指针)编译时无法预知。
在某 C/C++ 程序中,整型变量 a 的值为 0 且应用在表达式中,则最可能发生的情形是(50)。
查看答案与解析收起答案与解析
a=0 出现在除法之类的表达式里 → 除零。A——错,除零不是语法错误(语句结构合法)。B——错,编译阶段根本没有“异常”这个概念,异常是运行时的。C——错,运行时不可能“报告语法错误”(语法错误编译期就拦下了)。D——对,运行时除零产生异常——动态语义错误。
某程序运行时陷入死循环,则可能的原因是程序中存在(48)。
查看答案与解析收起答案与解析
死循环是运行时的行为——循环条件写错(如 `while (i < 10)` 里忘了 `i++`)。A、B——错,有词法/语法错误的程序根本编译不过,更跑不起来。D——错,静态语义错误编译时就能发现。C——对,程序能跑但行为错误 → 动态语义错误。
运行下面的 C 程序代码段,会出现(50)错误。

int i = 0;
while (i < 10);
i++;
- A. 语法 B. 语义 C. 词法 D. 运行时逻辑(动态语义)
查看答案与解析收起答案与解析
完整分析:`while (i < 10);` 末尾多了一个分号——循环体是空语句,i 永远是 0,条件永远成立,程序陷入死循环,`i++` 根本执行不到。
A——错,语法完全合法(空语句是合法的)。B——对(官方答案给的是 B“语义”类错误,即动态语义)。C——错,没有非法字符。D——运行时逻辑错误,本质上与 B 同义(动态语义错误)。
八、中间代码
中间代码是编译器“翻译到一半”的表示形式。三个必考点(教材 2.2.2 节原文要点):
- 常见形式:后缀式(逆波兰式)、三地址码、三元式、四元式、树(图)。
- 与机器无关:可以把不同高级语言翻译成同一种中间代码,可以跨平台;因此有利于进行与机器无关的优化处理、提高编译程序的可移植性。
- 中间代码不是每种编译器都必须的;它不能用正规式表示(正规式是词法层面的工具)。
将高级语言源程序翻译为机器语言程序的过程中常引入中间代码。以下关于中间代码的叙述中,错误的是(22)。
查看答案与解析收起答案与解析
A——对,与机器无关所以可跨平台。B——对,教材原话。C——对,不同高级语言可以翻成同一种中间代码(如都翻成三地址码)。D——错误:中间代码的价值恰恰在于不依赖具体机器的指令结构,如果“必须一致”就没有引入的意义了。选 D。
在对程序语言进行翻译的过程中,常采用一些与之等价的中间代码表示形式。常用的中间代码表示不包括(49)。
查看答案与解析收起答案与解析
A. 树——是(表达式树)。B. 后缀式——是(逆波兰式)。C. 四元式——是(还有三元式、三地址码)。D. 正则式——不是:正规式是词法分析的工具,描述的是单词符号的构成规则,与中间代码是两个层面。选 D。
将高级语言程序翻译为机器语言程序的过程中,常引入中间代码,其好处是(48)。
查看答案与解析收起答案与解析
A——错,反编译是另一个问题,与中间代码无关。B——对,教材原话级别的理由:中间代码与机器无关,优化可以在这个“通用层”上做,做完再翻到各种机器。C——错,多一层翻译形式不等于更快。D——错,引入中间层通常是“解耦”而非“降低复杂度”。
将高级语言源程序翻译成机器语言程序的过程中,常引入中间代码。以下关于中间代码的叙述中,不正确的是(22)。
查看答案与解析收起答案与解析
A——对。B——不正确:中间代码的形式是后缀式、三地址码、三元式、四元式、树——栈是求值后缀式的工具(数据结构),不是中间代码的表示形式。C——对。D——对,这正是“提高编译程序可移植性”的含义(移植编译器只需重写“中间代码 → 目标代码”的后端)。
将高级语言源程序先转化为一种中间代码是现代编译器的常见处理方式。常用的中间代码有后缀式、(22)、树等。
查看答案与解析收起答案与解析
五种形式默写——后缀式、三地址码、三元式、四元式、树。B. 三地址码——对(形如 `x = y op z`,最多三个地址)。A. 前缀码——是表达式的另一种表示(波兰式),但教材列举的是三地址码;C. 符号表——不是中间代码;D. 补码和移码——是机器数的数据表示。选 B。
将高级语言源程序通过编译或解释方式进行翻译时,可以先生成与源程序等价的某种中间代码。以下关于中间代码的叙述中,正确的是(22)。
查看答案与解析收起答案与解析
A——错,符号表是辅助结构,不是中间代码的表示形式。B——对,正是两种常用形式。C——错,与机器无关才对。D——自相矛盾:与机器无关恰恰是优化(与机器无关的优化)的前提。
九、正规式
正规式(正则表达式)描述的是字符串的集合(正规集),是词法分析的形式化工具。教材 2.2.2 节给出三个运算符及优先级:闭包 * 最高,连接(并置)次之,或 | 最低。
写字段(正规式)的统一套路:把串的“结构特征”翻译成运算符组合——先看开头/结尾有什么要求,再看中间字符怎么分组循环。
由 a、b 构造且仅包含偶数个 a 的串的集合用正规式表示为(49)。(选项为正规式公式图)
查看答案与解析收起答案与解析
分析思路:偶数个 a = 0 个、2 个、4 个……即 a 两两配对。把串按“每两个 a 之间全是 b”切段:每段形如 b*ab*a(消耗两个 a),整段循环若干次后允许再补一段纯 b*——结构为 `(b*ab*a)*b*` 一类。这正是选项 B(原选项为公式图)。
排除法:A、C、D 的结构都无法保证“a 成对出现”——例如含 `a*b*` 的式子允许奇数个 a。验证技巧:拿“aba”(2 个 a,合法)和“aba a”(3 个 a,不合法)分别代入选中的式子。
对于正规式 (0*1)*,其正规集中字符串的特点是(50)。
查看答案与解析收起答案与解析
分析思路:内层 `0*1` = “任意多个 0 + 一个 1”——每循环一次恰好消耗一个 1。外层闭包循环 n 次,1 就有 n 个;n 可以是 0(空串)。所以 1 的总数必须是偶数(含 0 个),而 0 的个数完全不受限制。
A——错,0 的个数随意(如 0001 只有 1 个 0 之外的 1,合法)。B——对。C——错,`00011`(0 连续)也合法,不要求交替。D——错,恰恰相反,1 被 0 段隔开不能连续。
某语言 L = {a^m·b^n | m ≥ 0,n ≥ 1},其正规表达式是(21)。(选项为公式图)
收起答案与解析查看答案与解析
翻译结构:m ≥ 0 个 a → `a*`;n ≥ 1 个 b → `bb*`(即 b⁺,至少一个 b)。所以正规式是 `a*bb*`(写作 a\*b\+),对应选项 B。
逐项排除的关键:n ≥ 1 意味着 b 至少一个——形如 `a*b*` 的式子允许“全是 a 没有 b”,排除;形如 `(a|b)*` 的式子允许 b 在 a 前面,也排除。
由字符 a、b 构成的字符串中,若每个 a 后至少跟一个 b,则该字符串集合可用正规式表示为(48)。(选项为公式图)
查看答案与解析收起答案与解析
分析思路:“每个 a 后面必须跟 b” → a 不能单独出现,只能以 `ab` 的形式出现。于是整串就是 `b` 和 `ab` 两种“积木”的任意拼接:`(b|ab)*`,对应选项 A。
验证:b ✓、ab ✓、abb ✓、abab ✓;ba ✗(a 后没跟 b)、aab ✗(aa 连续,第一个 a 后跟的是 a)。
排除法:含 `ab*` 的式子允许 a 后跟 0 个 b(即 a 单独出现)——不满足“至少跟一个”;含 `a(b|b*)` 一类的式子也类似。只有 A 同时保证“每个 a 必带 b”和“b 可以单独出现”。
在仅由字符 a、b 构成的所有字符串中,其中以 b 结尾的字符串集合可用正规式表示为(21)。(选项为公式图)
查看答案与解析收起答案与解析
分析思路:“以 b 结尾” = 任意 a、b 串(可空)+ 结尾一个 b:`(a|b)*b`,对应选项 D。
排除法:形如 `(a|b)*ab` 的式子要求结尾是 ab(b 前必有 a),排除了 "bb"、"bab" 这类以 b 结尾但结尾是 bb 的串;形如 `a(a|b)*b` 要求以 a 开头,也错。只有 `(a|b)*b` 对开头无限制。
十、有限自动机
有限自动机是词法分析的工具,用来识别正规集。教材 2.2.2 节的定义:
- 确定的有限自动机(DFA):每个状态识别一个字符后,只有一个转移状态。
- 不确定的有限自动机(NFA):某个状态识别一个字符后,有一个以上的转移状态(或含 ε 空转移)。
做题方法极其朴素:从初态出发,拿候选串逐字符走边;读完整个串停在终态 → 能识别。DFA 每步只有一条路,NFA 只要有一种走法能到终态就算数。
下图所示有限自动机的特点是(48)。(图为两状态 DFA:q0 自环 0,q0 经 1 到 q1,q1 经 0 回 q0)
查看答案与解析收起答案与解析
读图:q0 有 0 的自环(0 随便连);q0-1→q1 之后,q1 只有 0 出边回 q0——接受一个 1 之后,下一个字符只能是 0,再遇到 1 无路可走。
A——错,q0 上有 0 的自环,0 连续完全没问题。B——错,以 1 开头的串(如 10、101)也能被接受。C——错,以 0 开头的串(如 01、0101)也能被接受。D——对:q0-1→q1 之后,q1 上只有 0 出边,再遇到 1 无路可走——所以被识别的串中 1 不能连续出现。
下图所示的有限自动机中,0 是初始状态,3 是终止状态,该自动机可以识别(22)。
查看答案与解析收起答案与解析
逐串走图(这是 DFA,每步只有一条路):
aaaa:0 -a→ 2 -a→ 1 -a→ 2 -a→ 3(终态,且 3 有 a 自环)——识别成功。
abab:0-a→2-b→1-a→2-b→? 1 只有 b 出边回 2,走完停在 1,不是终态 ✗。baba:0-b→1,1 无 b 出边?1-b→2? 走完不在终态 ✗。abba:走完不在终态 ✗。选 B。
下图所示为一个有限自动机(其中,A 是初态、C 是终态),该自动机可识别(49)。
查看答案与解析收起答案与解析
读图:A 自环 1;A-0→B;B 自环 0;B-1→C(终态);C-0→B;C-1→A。
走 0101:A-0→B-1→C-0→B-1→C——停在终态 C ✓,选 C。其他三个串走完都不在终态。
下图所示为一个有限自动机(其中,A 是初态、C 是终态),该自动机所识别的字符串的特点是(48)。
查看答案与解析收起答案与解析
读图:A 自环 1;A-0→B;B 自环 0;B-1→C(终态);C-1→A;C-0→B。
分析结尾:要停在 C,最后一步必然是“B-1→C”;而到达 B 的最后一步是“A 或 C 经 0 到 B”——所以串的结尾必然是 `01`。
A、D 说以 10 结尾——错;B、C 表述一致(原文档两组重复),按官方答案选 C。
对高级语言源程序进行编译的过程中,有穷自动机(NFA 或 DFA)是进行(49)的适当工具。
查看答案与解析收起答案与解析
有限自动机与正规式等价,而正规式描述的是单词符号(标识符、常数、关键字)的构成规则——这正是词法分析的工作。语法分析用上下文无关文法(下推自动机),语义分析不做模式识别。选 A。
以下关于下图所示有限自动机的叙述中,不正确的是(49)。(图为两状态 FA:状态 1 为初态兼终态、自环 a;1-b→2,2-a→1)
查看答案与解析收起答案与解析
读图:状态 1(初态=终态)自环 a;1-b→2;2-a→1。
A——不正确:状态 1 上有 a 的自环,aaaa 一直在 1 上打转——a 连续出现完全没问题,选 A。B——正确:b 的出边唯一指向 2,而 2 的出边只有 a,所以两个 b 之间必有 a 隔开。C——正确:初态即终态,空串被接受。D——正确:b 后必到 2,2 后必是 a。
某非确定的有限自动机(NFA)的状态转换图如下图所示(既是初态也是终态),与该 NFA 等价的确定的有限自动机(DFA)是(49)。(NFA:初终态 q0 自环 a,q0-b→q1,q1-a→q0;选项为 DFA 图)

查看答案与解析收起答案与解析
先分析 NFA 识别什么:q0 是终态——串要么全由 a 组成(q0 自环),要么……最后一次转移落在 a 上时停在 q0(q1-a→q0)。也就是说:**被接受的串一定以 a 结尾**(b 永远不会是最后一个字符,因为 b 的目标是 q1,非终态)。
子集法确定化:初始集 {q0};读 a → {q0, q1}(q0 自环 a 且 q0-a→q1?——按图 q0-a→q0),读 b → {q1};{q1} 读 a → {q0},读 b → ∅。整理成两状态 DFA:终态上 a 自环、非终态由 b 进入——即“以 a 结尾”的 DFA,对应选项 A(原选项为 DFA 图)。
某非确定的有限自动机(NFA)的状态转换图如下图所示(既是初态也是终态)。以下关于该 NFA 的叙述中,正确的是(49)。(NFA:初终态 q0 自环 1,q0-0→q1,q1 自环 0,q1-1→q0)
查看答案与解析收起答案与解析
分析结尾:终态是 q0。到达/停留在 q0 的最后一步:要么 q0 自环读 **1**,要么 q1-1→q0 读 **1**——两条进 q0 的路最后读的都是 1!所以非空被识别串的结尾字符必然是 1,选 D。
逐项排除:A——错,长度随意("1" 长度 1)。B——错,"111" 0 个 0 也合法。C——错,"1" 开头是 1 也合法。
某确定的有限自动机(DFA)的状态转换图如下图所示(A 是初态,C 是终态),则该 DFA 能识别(49)。
查看答案与解析收起答案与解析
读图:A-a→B;B-b→C;C-a→B;C 自环 b。
走 abab:A-a→B-b→C-a→B-b→C——停在终态 ✓,选 B。aabb:A-a→B-b→C-b→C-a→B——停在 B ✗。baba:A-b→? 无边 ✗。abba:A-a→B-b→C-b→C-a→B ✗。
某确定的有限自动机(DFA)的状态转换图如下图所示(A 是初态,D、E 是终态),则该 DFA 能识别(48)。
查看答案与解析收起答案与解析
读图:A-0→B,A-1→C;B、C 之间有交叉边(B-1→C、C-0→B);B-0→D(D 自环 0,终态);C-1→E(E 自环 1,终态)。
走 11100:A-1→C-1→E-1→E-0→?——E 只有 1 自环,需要借助 C-0→B、B-0→D:按图实际路径为 A→C→E→E→? 官方答案 C(11100),考生按图逐字符走一遍即可确认停在终态。其余三串走完不在终态。
某确定的有限自动机(DFA)的状态转换图如下图所示(0 是初态,4 是终态),则该 DFA 能识别(49)。
查看答案与解析收起答案与解析
读图:0-a→1;1-a→3;3 自环 a、b;3-b→4(终态)。
走 aaab:0-a→1-a→3-a→3-b→4——停在终态 ✓,选 A。abab:0-a→1-a→3-b→4 之后还有字符 b 无路可走 ✗。bbba:0-b→2-b→2-a→? 2 无 a 出边 ✗(按图 2 只有 b 自环)。abba:走完不在终态 ✗。
下图所示为一个不确定有限自动机(NFA)的状态转换图。该 NFA 可识别字符串(49)。
查看答案与解析收起答案与解析
读图:S0(初态)自环 0 和 1;S0-1→S1;S1-0→S2;S2-1→S3(终态)。
走 0110:S0 用自环 0 吃掉第一个 0 → S0-1→S1-0→S2-1→S3,停在终态 ✓,选 A。NFA 的自环可以“吸收”任意长度的前缀——这是 NFA 题与 DFA 题手感的最大区别:只要后缀能走到终态就行。
下图所示为一个不确定有限自动机(NFA)的状态转换图,与该 NFA 等价的 DFA 是(49)。(NFA 含 ε 边:0 进入 1 链、ε 回环、以 0 结束;选项为 DFA 图)

查看答案与解析收起答案与解析
先分析 NFA 识别什么:0-0→1,1 经 ε 到 2,2-1→3,3 经 ε 可回 2(1 重复),3/2 经 ε 到 4,4-0→5(终态)。串形如 `0 1⁺ 0`——一个 0,中间至少一个 1,最后再一个 0。
子集法确定化:得到三状态 DFA——初态读 0 进中间态,中间态读 1 自环,读 0 进终态。对应选项 A(原选项为 DFA 图)。B/C/D 的 DFA 要么允许没有 1,要么多了别的转移。
某有限自动机的状态转换图如下图所示,该自动机可识别(50)。
查看答案与解析收起答案与解析
选 B(1100)。有限自动机的题只有一种做法:拿着字符串逐字符走图,走完停在终态才算识别。
走图规则:
① 从初态(带箭头的入边指向的那个圈)出发;
② 每读一个字符,沿标着该字符的边转移到下一个状态;
③ 字符串读完后,如果停在双圈(终态),则该串被识别;否则不被识别。
四个选项的判定:
A 0110:从初态读 0 后走的路径,后续字符无法继续转移,或最终不在终态。
B 1100:能沿边一路走到底,且最后停在终态——被识别。
C 1010:中途某一步没有对应的转移边,走不下去。
D 0101:走完不在终态。
两个技巧:一是先看串的首字符,初态有几条出边、分别标什么,就能立刻排除一部分选项;二是边读边在图上划,别在脑子里记状态,容易串。
原图在转换时丢失,但方法是通用的,对着任何一版真题图都能用。
下图所示有限自动机(DFA)是(49)。
查看答案与解析收起答案与解析
第一步:判 DFA/NFA。图上初态有 a、b 两个自环,又有一条 a 出边——同一个状态读 a 有**两条出路**(自环 a 和出边 a)→ **非确定的**(NFA),排除 A、B。
第二步:看识别串。沿图验证任何走法都无法以 bab 结尾停在终态 → “不能识别以 bab 结尾的串”。选 D。
下图所示为一个非确定的有限自动机(NFA),S0 为初态,S3 为终态。该 NFA 识别的字符串为(22)。
查看答案与解析收起答案与解析
读图:S0 自环 1(前面的 1 随便连);S0-1→S1;S1-0→S2;S2-1→S3(终态)。
分析:要停在 S3,最后三步必然是 1→0→1(进 S1 读 1、进 S2 读 0、进 S3 读 1)——串必须以 **101 结尾**;之前的部分被 S0 的 1 自环吸收。选 D。
排除法:01100(含连续 0)照样能识别(前缀在 S0 吸收不了 0?——0 会使 S0→S1……实际上 S0 只有 1 的出边,串前缀只能是 1;但官方答案聚焦“结尾 101”),A、B、C 均与图不符。
下图所示为两个有限自动机 M1 和 M2(A 是初态、C 是终态),(48)。
查看答案与解析收起答案与解析
逐个检查:M1:状态 A 上有 0 的自环,又有一条 0 出边到 B——读 0 有两条路 → NFA。M2:每个状态对每个输入字符的转移都唯一 → DFA。
选 D。判断口诀只有一条:**同一个状态读同一个字符,出边是否唯一**——唯一是 DFA,多条(或含 ε 边)是 NFA。
十一、正规式与有限自动机的转换
正规式和有限自动机是等价的:能写成正规式的语言一定能造出自动机来识别,反之亦然(教材 2.2.2 节)。考题就是给出自动机图选正规式(或反过来)。
下图所示为一个有限自动机(其中,A 是初态、C 是终态),该自动机识别的语言可用正规式(48)表示。(选项为公式图)

查看答案与解析收起答案与解析
读图:A 自环 1;A-0→B;B 自环 0;B-1→C(终态);C-0→B;C-1→A。
拆结构:从 A 出发的“圈”:单独的 1(自环),或 0(进 B)+ 若干 0(B 自环)+ 1(进 C)+ 1(回 A)——即串可划分成若干块,每块是“若干 0 后跟一个 1”或“单个 1”,整串读完停在 C。对应选项 A 的正规式(原选项为公式图)。
下图所示为一个不确定有限自动机(NFA)的状态转换图。该 NFA 识别的字符串集合可用正规式(48)描述。(选项为公式图)

查看答案与解析收起答案与解析
读图:a 进入中间链,b 在 ε 回环里重复任意次,最后 a 到终态——识别的串是 `ab*a`:两个 a 中间夹任意多个 b(也包括 aa,即 b 取 0 个)。
对应选项 A 的正规式(原选项为公式图)。验证:aa ✓、aba ✓、abba ✓;ba ✗(开头不是 a)、abab ✗(结尾不是 a)。
某有限自动机的状态转换图如下图所示,与该自动机等价的正规式是(50)。(选项为公式图)

查看答案与解析收起答案与解析
读图:初态 0 自环若干 0 后进 1,之后重复“若干 0 再一个 1”——串必须以 1 结尾,且每个 1 前面是一段 0。对应选项 B 的正规式(形如 `0*1(0*1)*` 一类的结构,原选项为公式图)。
十二、上下文无关文法
文法的四元组 G = (N, T, P, S):N 非终结符集合,T 终结符集合,P 产生式集合,S 开始符号。句子的定义(教材 2.2.2 节):从 S 出发推导出的、仅包含 T 中符号的符号串。
乔姆斯基分类(配合自动机记):
| 型 | 名称 | 对应自动机 | 用途 |
|---|---|---|---|
| 0 型 | 短语结构文法 | 图灵机 | — |
| 1 型 | 上下文有关文法 | 线性界限自动机 | — |
| 2 型 | 上下文无关文法 | 下推自动机 | 语法分析 |
| 3 型 | 正规文法 | 有限自动机 | 词法分析 |
口诀:0 图 1 线 2 下 3 有穷。程序设计语言的大多数语法用 **2 型(上下文无关文法)**描述。
设某语言的语法规则用上下文无关文法 G=(N, T, P, S) 表示,其中 N 是非终结符号的集合,T 是终结符号的集合,P 是产生式集合,S 是开始符号,那么符合该语言的句子是(50)。
查看答案与解析收起答案与解析
句子的定义有三要素——起点、终点、内容。A——对:从开始符号 S 推导、只含终结符 T。B——错,起点错了:从 N 中任意非终结符出发推出的是“句型的一部分”,不是句子。C——错,句子里不能含非终结符(V = N∪T,包含 N)。D——错,起点和内容都错。
由某上下文无关文法 M[S] 推导出某句子的分析树如下图所示,则错误的叙述是(50)。(图为语法树:S→aAcB,A、B 有子推导)
查看答案与解析收起答案与解析
A——错误。这棵树只能证明“存在一条以 a 开头的推导”,不能证明“所有句子都以 a 开头”——文法完全可能有其他产生式推出以别的字符开头的句子,“必须”太绝对。B——对,把树的叶子从左到右读出来就是句子 acabcbdcc。C——对,树的根结点展开用的就是这条产生式。D——对,树中不再展开的叶子都是终结符。
大多数程序设计语言的语法规则用(49)描述即可。
查看答案与解析收起答案与解析
A. 正规文法——太弱,描述不了嵌套结构(如括号配对)。B. 上下文无关文法——对,表达能力刚好覆盖程序语言的嵌套语法,且有高效的语法分析算法。C. 上下文有关文法——太强,分析器难以高效实现。D. 短语结构文法——最强,等价于图灵机。
对于大多数通用程序设计语言,用(50)描述其语法即可。
查看答案与解析收起答案与解析
选 B。这道题考的是乔姆斯基文法分类与程序设计语言语法的对应关系。
乔姆斯基把文法分四型,按描述能力从强到弱:
0 型(短语结构文法):能力最强,产生式 α→β 无限制,对应图灵机。
1 型(上下文有关文法):产生式形如 αAβ→αγβ,替换 A 要看上下文,对应线性有界自动机。
2 型(上下文无关文法):产生式形如 A→γ,左边必须是单个非终结符,与上下文无关,对应下推自动机。程序设计语言的语法(表达式、语句、程序结构)用它描述最合适。
3 型(正规文法):产生式形如 A→aB 或 A→a,对应有限自动机,只能描述词法(标识符、常数这类线性结构),描述不了嵌套。
所以通用程序设计语言的语法用 2 型(上下文无关文法)描述。
A 错:正规文法描述能力太弱,连“括号必须配对”这种嵌套都表达不了——这正是词法分析用正规式、语法分析用上下文无关文法的原因。
C 错:上下文有关文法能力过强,多用于自然语言研究,不是程序设计语言语法的常规工具。
D 错:短语结构文法(0 型)最强,但可判定性太差,编译器无法处理。
记忆:词法用 3 型(正规式/有限自动机),语法用 2 型(上下文无关文法/下推自动机)。
乔姆斯基(Chomsky)将文法分为 4 种类型,程序设计语言的大多数语法现象可用其中的(49)描述。
查看答案与解析收起答案与解析
选 B。程序设计语言的大多数语法现象用上下文无关文法(2 型)描述。
乔姆斯基四型的对应关系,配一句口诀“0 图 1 线 2 下 3 有穷”:
0 型(短语结构文法)→ 图灵机。产生式无限制,能力最强,但不可判定。
1 型(上下文有关文法)→ 线性界限自动机。产生式形如 αAβ→αγβ,替换 A 要看左右上下文。
2 型(上下文无关文法)→ 下推自动机。产生式形如 A→γ,左边只有一个非终结符,与上下文无关。程序设计的语法(嵌套结构、表达式、语句)靠它描述。
3 型(正规文法)→ 有穷自动机。产生式形如 A→aB 或 A→a,只能描述线性结构,用于词法。
A 错:上下文有关文法能力过强,多用于自然语言研究。C 错:正规文法太弱,连“括号配对”这种嵌套都表达不了。D 错:短语结构文法最强但无法实用。
为什么语法必须用 2 型?因为程序里有大量嵌套——括号嵌套、if 嵌套、函数调用嵌套。3 型只能处理有限状态,表达不了任意深度的嵌套;而 2 型配上栈(下推自动机)恰好能处理嵌套。
已知文法(S 为开始符号):S→0S1 | 1S0 | ε。从 S 出发可以推导出(21)。
查看答案与解析收起答案与解析
每条非空产生式都同时产生一个 0 和一个 1(S→0S1 两侧加、S→1S0 两侧加),所以推导出的串中 0、1 个数必然相等——但顺序受产生式限制(01、10、0011、1001、0101 可以;000111?可以:S→0S1→00S11→…;001?不行)。所以是“某些 0 和 1 个数相等的字符串”。
A、B——错,串里 0 和 1 个数相等,不可能全由单一字符构成。D——错,“个数不同”直接违反产生式结构。C——“某些”是关键词,个数相等且顺序合法的串才可推导。
简单算术表达式的结构可以用下面的上下文无关文法进行描述(E 为开始符号),(49)是符合该文法的句子。(产生式:E→T | E+T;T→F | T*F;F→-F | N;N 为数字;选项为表达式图)

查看答案与解析收起答案与解析
关键产生式:F→-F——允许对因子递归取负号,于是乘法的因子可以带负号(如 2\*-3 这类结构);T→T\*F——乘法左递归。
做题方法:把四个候选表达式从 E 开始逐步推导,推不下去的就是错的——错误的选项通常含有文法产生不出的结构(如除号、括号、连续运算符等,因为产生式里只有 +、\*、-(一元))。符合推导的只有 B(原选项为表达式图)。
程序设计语言的大多数语法现象可以用 CFG(上下文无关文法)表示。下面的 CFG 产生式集用于描述简单算术表达式(产生式中的运算符、id 表示单字母变量),那么符合该文法的表达式为(49)。(选项为表达式图)
查看答案与解析收起答案与解析
选 A(原产生式与选项为公式图)。判断“某个表达式能否由给定文法推导出来”,方法是从开始符号展开。
具体步骤:
① 从开始符号(通常是 E)出发;
② 每次选一个非终结符,用某条产生式的右部替换它;
③ 反复替换,直到得到一个只含终结符的串;
④ 如果这个串恰好等于选项中的表达式,说明它能被推导出来。
直观例子:文法 E → E+T | T、T → T*F | F、F → (E) | id,判断 a+b*c 能否推导:
E ⇒ E+T ⇒ T+T ⇒ F+T ⇒ a+T ⇒ a+T*F ⇒ a+F*F ⇒ a+b*c,成立。
而 a+b+ 这种缺少操作数的串,无论怎么展开都得不到——因为每条产生式都保证“运算符两边各有一个完整的子表达式”。
做题技巧:不必真的完整推导,先看选项里有没有文法明显产生不了的结构。文法里没有减号,选项出现减号就直接排除;文法不产生空操作数,出现也算排除。
设有描述简单算术表达式的上下文无关文法如下,其中 id 表示单字母:E→E+T | T,T→F*T | F,F→id。与使用该文法描述的表达式 a+b*c*d 相符的语法树为(48)。(选项为语法树图)

查看答案与解析收起答案与解析
推导:a+b\*c\*d 中,加法优先级低,所以 E→E+T:左 E 推出 a,右 T 推出 b\*c\*d。
b\*c\*d 用 T→F\*T 推导:T → F\*T,其中 F 推出 b(单字母),右边的 T 继续推 c\*d:T → F\*T,F 推出 c,T 推出 d(F→id)。
语法树特征:T→F\*T 是右递归——每个乘号的左孩子是单个字母(F),右孩子仍是一个 T;结合方式是 c\*d 先结合(在树中更深),再与 b 相乘。符合这个形状的语法树只有选项 A。
十三、中缀、后缀表达式
中缀式是我们平时写的 a+b;**后缀式(逆波兰式)**把运算符写在运算对象后面:ab+。两个必背结论(教材 2.2.2 节):
- 后缀式用栈求值:从左到右扫,运算数入栈,遇到运算符弹出栈顶运算、结果压回。
- 语法树的三种遍历:前缀式 = 先序遍历,中缀式 = 中序遍历,后缀式 = 后序遍历。
中缀转后缀的手算技巧:按优先级给中缀式加括号,然后把每个括号里的运算符移到括号后面,最后去掉所有括号。
逻辑表达式 a∧b∨c∧(b∨x>0) 的后缀式为(21)。(其中 ∧、∨ 分别表示逻辑与、逻辑或,> 表示大于)(选项为公式图)
查看答案与解析收起答案与解析
完整转换过程:∧ 优先级高于 ∨,先加括号:(a∧b) ∨ (c∧(b∨(x>0)))。
逐层转后缀:① x>0 → `x 0 >`;② b∨(x>0) → `b x 0 > ∨`;③ c∧(…) → `c b x 0 > ∨ ∧`;④ a∧b → `a b ∧`;⑤ 最后用 ∨ 连接两块:`a b ∧ c b x 0 > ∨ ∧ ∨`。
对应选项 D(原选项为公式图)。
算术表达式采用逆波兰式表示时不用括号,可以利用(20)进行求值。与逆波兰式对应的中缀表达式是(21)。(逆波兰式与选项为公式图)
查看答案与解析收起答案与解析
第(20)空:后缀式求值的过程——运算数入栈,运算符弹出栈顶两个数计算再把结果压回。整个过程只需要一个栈:数组不支持动态的“后进先出取数”,队列是先进先出,散列表更不沾边。选 B。
(21)把逆波兰式用栈展开即还原中缀式(遇运算符就把栈顶两个操作数用括号括起来合并),对应选项 C(原逆波兰式与选项为公式图)。
算术表达式 x-(y+c)*8 的后缀式是(22)。
查看答案与解析收起答案与解析
完整转换过程(加括号法):
① 按优先级加括号:x − ((y+c)×8);
② 最内层 (y+c) → `y c +`;
③ ((y+c)×8) → `y c + 8 ×`;
④ x − (…) → `x y c + 8 × −`,即 xyc+8\*-。
A. xy-c+8\* 对应 (x−y)+c 之后乘 8——运算次序错了;B. xyc-+8\* 对应 (x−(y+c))×8——减号位置错;C 与 D 的差别只在末尾运算符,C 是 +8 结尾(乘 8 后还加),对应 x−((y+c)\*8+8) 之类,错。
算术表达式 (a-b)*c+d 的后缀式是(22)。
查看答案与解析收起答案与解析
完整转换过程:加括号 (((a−b)×c)+d):① (a−b) → `a b −`;② ((a−b)×c) → `a b − c ×`;③ +d → `a b − c × d +`,即 ab-c\*d+。
A 与 C 相同(原文档两组重复,官方答案取 C);B. abc-\*d+ 对应 a−(b\*c)+d?——不对,它把 b、c 先入栈再减,运算对象配对错了;D. abcd-\*+ 对应 a−(b−(c+d)) 一类的结构,错。
算术表达式 a-b*c+d 的后缀式是(22)。
查看答案与解析收起答案与解析
完整转换过程:乘法优先级高,先算 b\*c;计算次序:① b\*c → `b c ×`;② a−(b\*c) → `a b c × −`;③ +d → `a b c × − d +`,即 abc-d\*+。
A. abc-\*d+ 对应 a−(b\*c)+d?——不是,abc-\*d+ 的减号作用在 a、b 上(a−b 后再乘 c 再加 d),对应 (a−b)\*c+d,错。C. ab-c\*d+ 正是 (a−b)\*c+d(2012 下原题),错。D 的结构完全不同。
算术表达式 (a-b)*(c+d) 的后缀式是(21)。
查看答案与解析收起答案与解析
完整转换过程:两个括号各自独立转后缀:(a−b) → `a b −`;(c+d) → `c d +`;乘号最后跟上:`a b − c d + ×`,即 ab-cd+\*。
B. abcd+\*- 对应 a−(b+c\*d) 一类结构;C. ab-\*cd+ 乘号插在两个括号中间——不是后缀式(后缀式的运算符必须紧跟操作数之后);D. ab-c+d\* 对应 ((a−b)+d)\*c,错。
表达式采用逆波兰式表示时,利用(22)进行求值。
查看答案与解析收起答案与解析
2011 上第 20 题原样重考,选 A(栈)。
另外,求值全过程演示,以 ab-c\*d+ 为例):a 入栈、b 入栈;遇 − 弹出 b、a 计算 a−b 压回;c 入栈;遇 × 弹出 c、(a−b) 计算 (a−b)×c 压回;d 入栈;遇 + 弹出 d、上式相加压回——栈顶即结果。整个过程只需一个栈。
表达式 (a-b)*(c+d) 的后缀式(逆波兰式)是(22)。
查看答案与解析收起答案与解析
2014 下第 21 题原样重考,选 D(ab-cd+\*)。
快速验算:(a−b) → ab−;(c+d) → cd+;乘号在最后 → ab−cd+\*。B 选项 ab-cd\*+ 对应 (a−b)\*c+d? 不对——它的乘号在 + 前面,对应 (a−b)\*c 之后再 +d?也不是,其结构是 ((a−b)\*(cd))+d 一类的错误排列。
算术表达式的后缀式为(20)。(原表达式与选项为公式图)
查看答案与解析收起答案与解析
选 A(原表达式与选项为公式图,转换时丢失)。这里把“中缀转后缀”的方法讲透,对着任何同类题都能做。
后缀式(逆波兰式)的规则是:操作数在前,运算符在后,不需要括号就能唯一确定运算顺序。
方法一:加括号法(最稳)
① 按运算优先级给原表达式加满括号,每个运算符一对;
② 把每个运算符移到它那对括号的右括号后面;
③ 去掉所有括号,得到的就是后缀式。
举例:a+b*c → 加括号 (a+(b*c)) → 移运算符 (a(bc)*)+ → 去括号 abc*+。
方法二:栈法(适合编程实现)
遇到操作数直接输出;遇到运算符,把栈顶优先级不低于它的运算符依次弹出并输出,再把自己压栈;遇到左括号直接压栈,遇到右括号弹到左括号为止。
常见错误:把 a-b-c 转成 abc--。正确是 ab-c-——减法是左结合,先算左边那个减号,所以它先输出。
十四、语法树与表达式
表达式、语法树、前缀/中缀/后缀式是一一对应的:后缀式 = 语法树的后序遍历(左子树 → 右子树 → 根);中缀式 = 中序遍历(必要时加括号);前缀式 = 先序遍历。
与算术表达式 (a+(b-c))*d 对应的树是(21)。(选项为二叉树图)

查看答案与解析收起答案与解析
分析运算次序:先 b−c(最内层括号),再与 a 相加(次内层),最后乘 d(最外层)。
树的结构:根是 \*(最后算的),左子树是 +(a 与 −(b,c)),右孩子是 d。只有选项 B 的树是这个形状,选 B。
下图为一个表达式的语法树,该表达式的后缀形式为(50)。(树:根 −,左子树 ÷(*(x,+(5,y)),a),右孩子 b;选项为公式图)

查看答案与解析收起答案与解析
后序遍历过程:左子树(÷ 结点)→ 右孩子(b)→ 根(−)。
左子树(÷)的后序:先 \* 子树(x、5、y、+、\*),再 a,再 ÷ ——展开为 `x 5 y + * a /`;然后 b;最后 −。
完整后缀式:`x5y+\*a/b-`,对应选项 A。
对于后缀表达式(其中 -、+、* 表示二元算术运算减、加、乘),与该后缀式等价的语法树为(22)。(选项为二叉树图)

查看答案与解析收起答案与解析
建树方法:后缀式从左到右扫——运算数建叶结点入栈;运算符弹出栈顶两棵子树作左右孩子(先弹的是右孩子),合并后入栈。
还原出的树:根 \*,左子树 +(a,b),右子树 −(c,d)——对应选项 C。
下面二叉树表示的简单算术表达式为(21)。(树:根 −,左子树 *(10,+(20,30)),右孩子 40;选项为表达式图)

查看答案与解析收起答案与解析
中序遍历加括号:左子树 \* 的操作数是 10 和 +(20,30)→ `10\*(20+30)`;右孩子 40;根 − 连接 → `10\*(20+30)-40`,选 C。
A. 10\*20+30-40——对应树根是 −、再 −,左斜结构,不符;B. 10\*(20+30-40)——40 应在 + 子树内,不符;D. 10\*20+(30-40)——40 跑到了减法里,不符。
某表达式的语法树如下图所示,其后缀式(逆波兰式)是(20)。(树形与选项为图)

查看答案与解析收起答案与解析
选 C(原树与选项为图)。语法树转后缀式的规则只有一条:做后序遍历——左子树 → 右子树 → 根。
为什么是后序?因为后缀式的定义就是“先操作数、后运算符”,而语法树里运算符在根、操作数在叶子。后序遍历恰好保证:处理完两个操作数(左右子树)之后才输出运算符(根)。
具体走法:
① 从根出发,先递归处理左子树——左子树内部同样“先左、再右、后根”;
② 再递归处理右子树;
③ 最后输出根节点的运算符。
三个常见混淆点:
前序遍历(根左右)对应前缀式(波兰式)。
中序遍历(左根右)对应中缀式,也就是平时写的表达式,需要括号辅助。
后序遍历(左右根)对应后缀式(逆波兰式)。
看图题先认准“后缀 = 后序 = 左右根”,再按这个顺序把叶子逐个写出来,不用管运算符优先级——树的结构已经把优先级编码进去了。
下图所示的二叉树表示的算术表达式是(20)(其中 *、/、- 表示乘、除、减运算)。(树:* → a 与 −;− → / 与 d;/ → b 与 c;选项为表达式图)

查看答案与解析收起答案与解析
中序遍历加括号:左子树 \* 的操作数是 a 和 − 子树;− 的操作数是 /(b,c)和 d → 左子树 = `a\*(b/c)`;根 − 连接右孩子 d → `a\*(b/c)-d`,选 C。
十五、杂题选讲
剩下的题考的是“程序设计语言常识”:语言的特点与分类、存储分配、标记语言、编译流程……靠积累,也靠排除法。
已知某高级语言源程序 A 经编译后得到机器 C 上的目标程序 B,则(21)。
查看答案与解析收起答案与解析
A——对。编译是有损翻译:注释丢了、变量名变成了地址、控制流被优化重排——反编译无法还原出源程序 A 的原始形态。B——错,反汇编(目标代码 → 汇编代码)是机械的一一对应,能得到等价的汇编代码。C——错,反编译得到的是“功能等价的高级代码”,不是原始的变量声明和算法流程。D——错,交叉编译是“在 A 机器上编译出 B 机器的目标程序”,与动态链接库的产生方式无关。
下面关于程序语言的叙述,错误的是(22)。
查看答案与解析收起答案与解析
A——对,脚本语言是动态语言,类型和结构可在运行时变。B——对,脚本语言靠引擎解释执行。C——错误:PHP、JavaScript 是脚本语言,属于动态语言而非静态语言,其类型在运行时才确定。D——对,C 是静态语言的典型。
以下关于 C/C++ 语言指针变量的叙述中,正确的是(21)。
查看答案与解析收起答案与解析
A——对,指针变量本身也是变量,全局、局部皆可。B——错,指针变量只存地址(大小固定,如 4/8 字节),与所指对象的大小无关。C——错,指针加减整数在 C 里有明确定义(按所指类型的大小移动),遍历数组全靠它。D——错,指针可以指向静态变量、局部变量、数组元素,不必是动态对象。
若一个程序语言可以提供链表的定义和运算,则其运行时的(49)。
查看答案与解析收起答案与解析
A——错,链表结点运行时动态增减,静态分配(编译时定死)撑不住。B——对,链表结点运行时动态产生、动态释放——这正是堆分配的用武之地。C、D——错,指令空间与数据结构无关,也不会放“堆区”。
标记语言用一系列约定好的标记来对电子文档进行标记,以实现对电子文档的语义、结构及格式的定义。(49)不是标记语言。
查看答案与解析收起答案与解析
A. HTML——是(网页标记语言)。B. XML——是(可扩展标记语言)。C. WML——是(无线应用协议的网页标记语言)。D. PHP——不是,它是脚本编程语言(服务端动态网页处理),用来写逻辑而不是标记文档。
以下关于可视化程序设计的叙述中,错误的是(49)。
查看答案与解析收起答案与解析
A——错误,“无需编写程序代码”太绝对——可视化只是把界面搭建图形化,业务逻辑代码还是要写。B——对,控件 + 事件驱动正是可视化的两个支柱。C——对,拖拽控件的比喻。D——对,解释方式即写即现。
以下关于汇编语言的叙述中,错误的是(50)。
查看答案与解析收起答案与解析
A——对,指令语句就是要翻译的主体。B——错误:伪指令(段定义、数据定义等)是给汇编程序的控制命令,指示汇编过程,不生成机器代码。C——对,汇编程序的定义。D——对,某些指令(如 NOP、RET)只有操作码没有操作数。
可用于编写独立程序和快速脚本的语言是(20)。
查看答案与解析收起答案与解析
A. Python——对,既能写独立程序,又是快速脚本的典型。B. Prolog——逻辑型语言(人工智能/专家系统)。C. Java、D. C#——偏重型工程开发,写“快速脚本”不合适。
在对高级语言源程序进行编译的过程中,为源程序中变量所分配的存储单元的地址属于(22)。
查看答案与解析收起答案与解析
A. 逻辑地址——对,编译时变量分配的是相对地址(从 0 开始编),程序加载运行时才由系统映射到物理地址。B. 物理地址——错,那是内存芯片上的真实地址,编译器看不到。C——接口地址是设备概念。D. 线性地址——是 x86 分段后的中间概念,不是编译期分配的。
以下关于语言 L 的叙述中,正确的是(48)。(L 为某字符串集合,原题用公式给出)
查看答案与解析收起答案与解析
先记一条铁律:正规式与有限自动机等价——能写正规式就一定能造自动机,反之亦然。所以 A、C 这种“一能一不能”的选项直接排除。
再判 L:题中 L 的结构(需要计数的串集合)超出了正规集的表达能力——正规式只能表达“有限自动机级别”的规律,计数类语言(如 aⁿbⁿ)不是正规集。选 D。
弱类型语言(动态类型语言)是指不需要进行变量/对象类型声明的语言。(50)属于弱类型语言。
查看答案与解析收起答案与解析
A. Java——强类型,变量必须声明类型。B. C/C++——强类型。C. Python——对,变量不用声明类型,`x = 1` 之后 `x = "abc"` 也合法。D. C#——强类型。
以下程序设计语言中,(20)更适合用来进行动态网页处理。
查看答案与解析收起答案与解析
A. HTML——是标记语言,不是编程语言。B. LISP——函数式语言(人工智能)。C. PHP——对,服务端动态网页的典型语言。D. JAVA/C++——通用编程语言。
C 程序中全局变量的存储空间在(22)分配。
查看答案与解析收起答案与解析
A. 代码区——放指令,不放数据。B. 静态数据区——对,全局变量和 static 变量在编译时分配,程序运行期间一直存在。C. 栈区——放局部变量和函数调用信息,函数返回即释放。D. 堆区——放 malloc/new 动态申请的空间。
递归下降分析方法是一种(50)方法。
查看答案与解析收起答案与解析
语法分析方法分两族——自上而下:递归下降、预测分析(LL(1));自底向上:移进-归约、LR 系列。递归下降从开始符号往下推导,是自上而下;且它属于语法分析(不是词法分析),选 B。
以下关于脚本语言的叙述中,正确的是(21)。
查看答案与解析收起答案与解析
A——错,脚本语言是专用性较强的语言(嵌入应用、自动化),不是通用语言。B——错,系统级开发要求效率,用 C/C++。C——对,脚本语言的标配。D——错,Python/JS 都能定义和调用函数。
移进—归约分析法是编译程序(或解释程序)对高级语言源程序进行语法分析的一种方法,属于(48)的语法分析方法。
查看答案与解析收起答案与解析
选 B(自底向上)。移进—归约分析的过程,本质是从叶子往根拼语法树。
具体过程:
① 把输入符号逐个移进(shift)到分析栈里;
② 一旦栈顶若干符号恰好构成某个产生式的右部,就把它们归约(reduce)成该产生式的左部(一个非终结符);
③ 重复移进和归约,直到整个输入被归约成开始符号。
为什么是自底向上?因为归约的方向是“先把具体的串收拢成抽象的非终结符”,相当于在语法树上先构造叶子、再逐层往上合并到根。
A 错:自顶向下是另一种思路——从开始符号出发,不断用产生式展开,试图匹配输入串。递归下降分析法、LL(1) 分析法都属于这一类。
C、D 错:自左向右/自右向左说的是扫描输入的方向,不是分析树的构造方向。所有实用的语法分析器都从左到右扫描,这个维度区分不了方法。
对照记忆:自顶向下 = 推导(展开),自底向上 = 归约(收拢)。LR 分析法、算符优先分析法都属于自底向上。
在高级语言源程序中,常需要用户定义的标识符为程序中的对象命名,常见的命名对象有(20)。①关键字(或保留字)②变量 ③函数 ④数据类型 ⑤注释
查看答案与解析收起答案与解析
①——错,关键字(int、if、while)是语言保留的,不能用作用户自定义标识符。②——对,变量要命名。③——对,函数要命名。④——对,自定义类型(typedef、class)要命名。⑤——错,注释不参与编译,根本不需要标识符。
更适合用来开发操作系统的编程语言是(20)。
查看答案与解析收起答案与解析
A. C/C++——对,指针直接操作硬件、编译成原生代码、效率极高——操作系统、驱动、嵌入式全是它。B. Java——有 JVM 隔离,不方便直接摸硬件。C. Python——解释执行,太慢。D. JavaScript——浏览器脚本。
以下关于程序设计语言的叙述中,不正确的是(21)。
查看答案与解析收起答案与解析
A——不正确:脚本语言当然能用变量和函数(Python、JS 天天在用)。B——对,标记语言(HTML)描述格式与链接。C——对。D——对,编译成原生代码效率更高。
(20)是一种函数式编程语言。
查看答案与解析收起答案与解析
四种范型对号入座——函数式:Lisp(还有 Haskell、Erlang);逻辑式:Prolog;面向对象:Java、C++;脚本/多范型:Python。选 A。
语法指导翻译是一种(50)方法。
查看答案与解析收起答案与解析
语法指导翻译= 在**语法分析**的同时执行**语义动作**(计算属性、生成中间代码)——它发生在编译期,属于**静态语义分析**的方法。选 C。A——错,动态语义分析发生在运行时(实际不存在这个词,编译期做不了运行时的事)。B、D——错,它是生成/检查的方法,不是优化的方法。
计算机执行程序时,内存分为静态数据区、代码区、栈区和堆区。其中(48)一般在进行函数调用和返回时由系统进行控制和管理,(49)由用户在程序中根据需要申请和释放。
查看答案与解析收起答案与解析
(48)函数调用/返回时,局部变量和调用信息在栈上自动压入弹出——由系统控制管理,选 C。(49)用户程序根据需要 malloc/new 申请、free/GC 释放的是堆区,选 D。
四个区一句话:代码区放指令;静态数据区放全局/static 变量(编译时分配);栈区系统管(自动);堆区用户管(手动)。
用 C/C++ 语言为某个应用编写的程序,经过(21)后形成可执行程序。
查看答案与解析收起答案与解析
C/C++ 的四步固定顺序——预处理(展开宏定义、头文件、条件编译,产出 .i)→ 编译(翻译成汇编,产出 .s)→ 汇编(翻译成机器码目标文件,产出 .o)→ 链接(拼装目标文件与库,产出可执行文件)。选 A。
Java 语言符合的特征有(22)和自动的垃圾回收处理。①采用即时编译 ②采用静态优化编译 ③对象在堆空间分配 ④对象在栈空间分配
查看答案与解析收起答案与解析
①——对,Java 源码编译成字节码后,JVM 在运行时对热点代码做即时编译(JIT)。②——错,“静态优化编译”不是 Java 的运行特征。③——对,Java 的对象实例都在堆空间分配(栈上只放对象的引用)。④——错。选 A。
Python 语言的特点不包括(48);在 Python 语言中(49)是一种可变的、有序的序列结构,其中元素可以重复;在 Python 语言的模块中,(50)不支持深度学习模型。
查看答案与解析收起答案与解析
第(48)空:Python 是解释型脚本语言,“编译型”不属于它的特点——选 B。跨平台开源、面向对象、动态类型都是 Python 的标签。
第(49)空:A. 元组——有序但不可变;B. 字符串——有序但不可变;C. 列表——对,可变、有序、可重复;D. 集合——可变但无序且去重。选 C。
第(50)空:TensorFlow、PyTorch、Keras 都是深度学习框架;Matplotlib 是画图(可视化)库,不支持深度学习模型——选 B。
十六、考前速记:25 条“看到就选”
- 编译生成独立目标程序;解释不生成、参与运行控制、速度慢。
- 词法、语法、语义分析三个阶段编译解释都要做,顺序不可换。
- 编译可省略中间代码生成和代码优化;解释没有这三个阶段。
- 词法分析:字符流 → 记号流;语法分析:记号流 → 语法树;语义分析:类型检查。
- 语法分析能查出所有语法错误;语义分析只能查出静态语义错误。
- 动态语义错误(死循环、除零、数组越界)运行时才能发现。
- 目标代码生成与机器密切相关,寄存器分配在这个阶段。
- 符号表贯穿编译全程,收集记录符号的类型与特征信息。
- 中间代码:后缀式、三地址码、三元式、四元式、树——与机器无关,利于优化和移植。正规式不是中间代码。
- 正规式与有限自动机等价,是词法分析的工具。
- 语法分析用上下文无关文法;文法口诀 0 图 1 线 2 下 3 有穷。
- 语法分析两族:自上而下(递归下降、LL);自底向上(移进-归约、LR)。
- 传值:实参的值 → 形参,单向;实参可以是常量、变量、表达式。
- 传引用:实参的地址 → 形参,双向;实参必须有地址,不能是常量或表达式。
- 计算题口诀:标出“副本/本体”,逐行执行,改引用就是改实参。
- 变量有存储单元可改变;常量不能改,但常量也有类型。
- 数据类型三作用:分配存储单元、检查运算对象、规定取值范围和运算——与动态数据结构无关。
- 全局/static 变量在静态数据区;局部变量在栈;new/malloc 在堆。
- 三种基本控制结构:顺序、选择、循环(重复)——递归不算。
- 后缀式(逆波兰式)用栈求值;= 语法树的后序遍历。
- 中缀转后缀:按优先级加括号,运算符移到括号后,去括号。
- 正规式优先级:闭包
*> 连接 > 或|。 - DFA 每个状态每个字符只有一条出边;NFA 有多条(或有 ε 边)。
- 有限自动机识别串:从初态逐字符走,读完停在终态即接受。
- C/C++ 生成可执行程序:预处理 → 编译 → 汇编 → 链接。
十七、答案速查表
编译与解释
| 年份 | 题号 | 答案 | 年份 | 题号 | 答案 |
|---|---|---|---|---|---|
| 2012 下 | 48 | B | 2013 上 | 20 | C |
| 2014 上 | 48 | A | 2016 上 | 20 | A |
| 2018 上 | 21 | C |
语言基本成分(控制结构、数据类型、常量变量、逻辑表达式)
| 年份 | 题号 | 答案 | 年份 | 题号 | 答案 |
|---|---|---|---|---|---|
| 2010 上 | 33 | B | 2019 上 | 20 | A |
| 2009 下 | 20 | C | 2011 上 | 22 | B |
| 2011 下 | 28 | B | 2018 下 | 22 | C |
| 2010 下 | 20 | B | 2015 上 | 20 | C |
| 2012 上 | 20 | C | 2016 下 | 20 | B |
| 2018 下 | 20 | B |
传值调用与传引用调用
| 年份 | 题号 | 答案 | 年份 | 题号 | 答案 |
|---|---|---|---|---|---|
| 2009 上 | 20 | C | 2011 上 | 50 | C |
| 2012 上 | 50 | D | 2013 上 | 21 | C |
| 2013 下 | 20 | A | 2014 上 | 21 | B |
| 2016 下 | 21 | C | 2011 下 | 49、50 | B、D |
| 2015 下 | 50 | A | 2016 上 | 50 | D |
| 2017 上 | 49 | B | 2017 下 | 50 | D |
| 2018 下 | 50 | D | 2019 上 | 50 | B |
| 2020 下 | 48 | A | 2021 上 | 50 | C |
编译程序基本原理、符号表、词法语法语义、程序错误
| 年份 | 题号 | 答案 | 年份 | 题号 | 答案 |
|---|---|---|---|---|---|
| 2010 上 | 48 | C | 2012 上 | 51 | C |
| 2015 上 | 48 | D | 2015 下 | 20、21 | C、B |
| 2013 上 | 49、50 | A、C | 2014 上 | 22 | A |
| 2019 上 | 22 | B | 2021 下 | 21 | B |
| 2009 下 | 48 | C | 2010 上 | 22 | A |
| 2010 下 | 21 | A | 2011 上 | 48 | D |
| 2014 下 | 48 | D | 2017 上 | 22 | B |
| 2017 下 | 48 | C | 2018 下 | 48 | B |
| 2019 上 | 21、48 | C、A | 2019 下 | 20、21 | A、B |
| 2021 上 | 21 | B | 2011 下 | 20 | D |
| 2012 下 | 50 | A | 2014 上 | 50 | D |
| 2015 下 | 48 | C | 2016 下 | 50 | D |
中间代码
| 年份 | 题号 | 答案 | 年份 | 题号 | 答案 |
|---|---|---|---|---|---|
| 2009 下 | 22 | D | 2012 下 | 49 | D |
| 2013 下 | 48 | B | 2014 下 | 22 | D |
| 2016 上 | 22 | B | 2017 下 | 22 | B |
正规式与有限自动机
| 年份 | 题号 | 答案 | 年份 | 题号 | 答案 |
|---|---|---|---|---|---|
| 2009 上 | 49 | B | 2010 上 | 50 | B |
| 2012 下 | 21 | B | 2016 下 | 48 | A |
| 2017 上 | 21 | D | 2009 上 | 48 | D |
| 2010 下 | 22 | B | 2011 上 | 49 | C |
| 2012 上 | 48 | C | 2013 下 | 49 | A |
| 2014 下 | 49 | A | 2015 上 | 49 | A |
| 2015 下 | 49 | D | 2016 上 | 49 | B |
| 2017 上 | 48 | C | 2017 下 | 49 | A |
| 2018 下 | 49 | A | 2019 上 | 49 | A |
| 2020 下 | 50 | B | 2021 上 | 49 | D |
| 2021 下 | 22 | D | 2010 下 | 48 | D |
| 2011 下 | 48 | A | 2018 上 | 48 | A |
| 2019 下 | 50 | B |
上下文无关文法
| 年份 | 题号 | 答案 | 年份 | 题号 | 答案 |
|---|---|---|---|---|---|
| 2009 上 | 50 | A | 2009 下 | 50 | A |
| 2013 下 | 21 | C | 2014 上 | 49 | B |
| 2014 下 | 50 | B | 2016 下 | 49 | B |
| 2018 上 | 49 | B | 2020 下 | 49 | A |
| 2021 上 | 48 | A |
中缀、后缀表达式与语法树
| 年份 | 题号 | 答案 | 年份 | 题号 | 答案 |
|---|---|---|---|---|---|
| 2010 上 | 21 | D | 2011 上 | 20、21 | B、C |
| 2012 上 | 22 | D | 2012 下 | 22 | C |
| 2013 下 | 22 | B | 2014 下 | 21 | A |
| 2015 下 | 22 | A | 2019 下 | 22 | D |
| 2021 上 | 20 | A | 2015 上 | 21 | B |
| 2017 上 | 50 | A | 2018 上 | 22 | C |
| 2018 下 | 21 | C | 2020 下 | 20 | C |
| 2021 下 | 20 | C |
杂题
| 年份 | 题号 | 答案 | 年份 | 题号 | 答案 |
|---|---|---|---|---|---|
| 2009 上 | 21、22 | A、C | 2009 下 | 21、49 | A、B |
| 2010 上 | 49 | D | 2010 下 | 49、50 | A、B |
| 2012 下 | 20 | A | 2013 上 | 22、48 | A、D |
| 2013 下 | 50 | C | 2014 上 | 20 | C |
| 2015 上 | 22、50 | B、B | 2016 上 | 21、48 | C、B |
| 2017 上 | 20 | B | 2017 下 | 20、21 | A、A |
| 2018 上 | 20、50 | A、C | 2019 下 | 48、49 | C、D |
| 2020 下 | 21 | A | 2021 上 | 22 | A |
| 2021 下 | 48、49、50 | B、C、B |
参考资料
- 《软件设计师教程(第5版)》第 2 章“程序设计语言基础知识”(2.1 程序设计语言概述、2.2 语言处理程序基础)——本篇所引教材内容的出处。
- 希赛网《软件设计师考试知识点 8——程序设计语言与语言处理程序基础》:传值/传址、后缀表达式、有限自动机的考法分析。
- CSDN 备考笔记《程序设计语言基础知识点(软件设计师)》《【软件设计师】程序设计语言》:编译过程表、文法口诀“0 图 1 线 2 下 3 有穷”、静态/动态与强/弱类型对照。
- 名校题库网《2018 年下半年软件设计师上午真题》:真题 16、31、32 的原始表述核对。
写在最后
程序设计语言这一章的“魂”只有一句话:高级语言怎么变成机器能跑的程序,以及这个过程中每个环节在干什么。
- 编译和解释的分界,就是“有没有独立的目标程序、谁参与运行”;
- 传值和传引用的分界,就是“给的是值还是地址、能不能改到实参”;
- 六个阶段的分界,就是“字符流 → 记号流 → 语法树 → 中间代码 → 目标代码”这条流水线上的每一站;
- 正规式、自动机、文法,则是这条流水线两端(词法和语法)的形式化描述。
计算题(传引用、后缀式、自动机识别)看着吓人,其实全是一步步走流程的体力活;概念题看着零散,其实都挂在这条主线上。把上面的 145 个空刷两遍,把速记清单默一遍,这一章的 6~8 分就稳了。
祝备考顺利。
还没有评论 — 第一条由你来留。