如果说第一章是“背多分”,那第二章就是“懂多分”——程序设计语言是整张上午卷里最像一门课的章节:编译原理的骨架(词法、语法、语义、中间代码)、形式语言的皮毛(正规式、自动机、文法)、再加一点真实编程语言的经验(传值传引用、栈和堆、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★★靠语言常识

三条主线,先把它们抓住:

  1. 翻译的主线:高级语言 → 词法分析(字符流变记号流)→ 语法分析(记号流变语法树)→ 语义分析(类型检查)→ 中间代码 → 优化 → 目标代码。解释方式只做前三步,然后直接执行。
  2. 调用的主线:传值是把“值”抄一份给形参(单向),传引用是把“地址”给形参(双向)——所有计算题都是这一句话的展开。
  3. 形式语言的主线:正规式 ↔ 有限自动机(管词法),上下文无关文法(管语法),后缀式(树的 后序遍历)。

一、编译程序与解释程序

这是全章最基础的一组概念,考试只围绕一个核心反复出题:编译生成独立的目标程序,解释不生成、且参与运行控制。

教材(2.1.1 节)对二者的定义是本章所有题的“母答案”:

  • 解释程序:翻译源程序时不生成独立的目标程序;解释程序和源程序要参与到程序的运行过程中(运行控制权在解释器手里)。
  • 编译程序:翻译时把源程序翻译成独立保存的目标程序;机器上运行的是与源程序等价的目标程序,源程序和编译程序都不再参与目标程序的运行过程。

把这张对比表记熟,五个空是白送的:

对比项编译方式解释方式
是否生成目标程序生成,独立保存不生成独立目标程序
谁参与运行源程序和编译器都不参与解释程序和源程序参与运行,控制权在解释器
执行速度快慢
典型语言C、C++Python、JavaScript、PHP
词法/语法/语义分析都要做,顺序不可交换也都要做(这一点最容易错)
真题 1 2012 下半年 · 题 48

将高级语言源程序翻译成目标程序的是( )。

  • A解释程序
  • B编译程序
  • C链接程序
  • D汇编程序
查看答案与解析收起答案与解析
答案B

A. 解释程序——错。解释程序对源程序边翻译边执行,不生成独立的目标程序,与题干“翻译成目标程序”不符。B. 编译程序——对。教材 2.1.1 节:编译程序将高级语言源程序翻译成“独立保存的目标程序”。C. 链接程序——错。链接器处理的是编译产生的目标模块和库文件,把它们拼装成可执行文件,它不做“高级语言 → 目标程序”的翻译。D. 汇编程序——错。汇编程序的输入是汇编语言源程序,不是高级语言源程序。

真题 2 2013 上半年 · 题 20

以下关于解释程序和编译程序的叙述中,正确的是( )。

  • A编译程序和解释程序都生成源程序的目标程序
  • B编译程序和解释程序都不生成源程序的目标程序
  • C编译程序生成源程序的目标程序,而解释程序则不然
  • D编译程序不生成源程序的目标程序,而解释程序反之
查看答案与解析收起答案与解析
答案C

A——错,“都生成”否认了解释方式的特征。B——错,“都不生成”否认了编译方式的特征。C——对,与教材定义完全一致。D——错,两个方向都说反了。

真题 3 2014 上半年 · 题 48

以下关于实现高级程序设计语言的编译和解释方式的叙述中,正确的是( )。

  • A在编译方式下产生源程序的目标程序,在解释方式下不产生
  • B在解释方式下产生源程序的目标程序,在编译方式下不产生
  • C编译和解释方式都产生源程序的目标程序,差别是优化效率不同
  • D编译和解释方式都不产生源程序的目标程序,差别在是否优化
查看答案与解析收起答案与解析
答案A

A——对,教材原话的复述。B——错,说反了。C——错,“差别是优化效率不同”是干扰说法,本质差别在是否生成目标程序、是否参与运行。D——错,同 B。

真题 4 2016 上半年 · 题 20

以下关于高级程序设计语言实现的编译和解释方式的叙述中,正确的是( )。

  • A编译程序不参与用户程序的运行控制,而解释程序则参与
  • B编译程序可以用高级语言编写,而解释程序只能用汇编语言编写
  • C编译方式处理源程序时不进行优化,而解释方式则进行优化
  • D编译方式不生成源程序的目标程序,而解释方式则生成
查看答案与解析收起答案与解析
答案A

A——对。教材强调:编译方式下“源程序和编译程序都不再参与目标程序的运行过程”;解释方式下“解释程序和源程序要参与到程序的运行过程中”。B——错。编译器和解释器都可以用任何语言实现(教材举过用高级语言写编译器的例子;GCC 编译器本身就是 C 写的)。C——错,代码优化是编译阶段的产物,解释方式谈不上优化。D——错,生成关系反了。

真题 5 2018 上半年 · 题 21

将高级语言源程序翻译为可在计算机上执行的形式有多种不同的方式,其中,( )。

  • A编译方式和解释方式都生成逻辑上与源程序等价的目标程序
  • B编译方式和解释方式都不生成逻辑上与源程序等价的目标程序
  • C编译方式生成逻辑上与源程序等价的目标程序,解释方式不生成
  • D解释方式生成逻辑上与源程序等价的目标程序,编译方式不生成
查看答案与解析收起答案与解析
答案C

还是同一句话,只是把“目标程序”换成了“逻辑上与源程序等价的目标程序”——这是教材对编译产物“等价性”的强调。C 对;A、B、D 的排列组合均与定义矛盾。

二、程序设计语言的基本成分

教材 2.1.2 节把语言的基本成分分成四类:数据、运算、控制、传输。这一块的题都很浅,但有两个高频细节——数据类型的作用、逻辑表达式的短路求值。

控制结构:教材指出程序的三种基本控制结构是顺序、选择、循环(重复)。注意“递归”不是基本控制结构,它是程序的一种调用方式。

数据类型的作用(教材原文三条):① 便于为数据合理分配存储单元;② 便于对参与表达式计算的数据对象进行检查;③ 便于规定数据对象的取值范围及能够进行的运算。“定义动态数据结构”不在其列——动态数据结构(链表、树)靠的是指针和堆分配,跟“数据必须有类型”没有因果关系。

常量与变量(教材 2.1.2 数据成分):常量在程序运行时不能改变、不分配存储单元;变量运行时可以改变、有对应的存储单元。但要注意:常量也有类型——2010 下那道题的坑就在这。

逻辑表达式的短路求值:&&(与)左边为假,整体为假,右边不算;||(或)左边为真,整体为真,右边不算;! 优先级最高且右结合,&&、|| 左结合。

真题 6 2010 上半年 · 题 33

程序的三种基本控制结构是( )。

  • A过程、子程序和分程序
  • B顺序、选择和重复
  • C递归、堆栈和队列
  • D调用、返回和跳转
查看答案与解析收起答案与解析
答案B

A. 过程、子程序和分程序——错,这些是程序的组织单位(结构化程序设计的模块概念),不是控制结构。B. 顺序、选择和重复——对,“重复”就是循环,教材控制成分的原话。C. 递归、堆栈和队列——错,递归是一种调用方式,堆栈、队列是数据结构。D. 调用、返回和跳转——错,这些是更底层的执行机制。

真题 7 2019 上半年 · 题 20

通用的高级程序设计语言一般都会提供描述数据、运算、控制和数据传输的语言成分,其中,控制包括顺序、( )和循环结构。

  • A选择
  • B递归
  • C递推
  • D函数
查看答案与解析收起答案与解析
答案A

A. 选择——对(if/switch)。B. 递归——错,它是函数调用自身的方式。C. 递推——错,是算法思想(如动态规划的递推式)。D. 函数——错,属于传输/组织成分。

真题 8 2009 下半年 · 题 20

许多程序设计语言规定,程序中的数据都必须具有类型,其作用不包括( )。

  • A便于为数据合理分配存储单元
  • B便于对参与表达式计算的数据对象进行检查
  • C便于定义动态数据结构
  • D便于规定数据对象的取值范围及能够进行的运算
查看答案与解析收起答案与解析
答案C

A——是类型的作用(int 分 4 字节、char 分 1 字节)。B——是(编译期检查“整型不能与字符串相加”)。C——不是。动态数据结构(链表、树)能否定义,取决于语言是否提供指针/引用和堆分配,与“数据必须有类型”没有因果关系——C 语言有类型也有指针,JavaScript 没有静态类型照样有动态结构。D——是(类型规定了取值范围和支持的运算)。

真题 9 2011 上半年 · 题 22

若一种程序设计语言规定其程序中的数据必须具有类型,则有利于( )。①在翻译程序的过程中为数据合理分配存储单元 ②对参与表达式计算的数据对象进行检查 ③定义和应用动态数据结构 ④规定数据对象的取值范围及能够进行的运算 ⑤对数据进行强制类型转换

  • A①②③
  • B①②④
  • C②④⑤
  • D③④⑤
查看答案与解析收起答案与解析
答案B

①——对(对应作用一)。②——对(对应作用二)。③——错(同真题 8 的分析)。④——对(对应作用三)。⑤——错,强制类型转换是语言提供的运算手段,教材的三条作用里没有把它列为“规定类型”的好处。

真题 10 2011 下半年 · 题 28

某程序设计语言规定在源程序中的数据都必须具有类型,然而,(28)并不是做出此规定的理由。

  • A为数据合理分配存储单元
  • B可以定义和使用动态数据结构
  • C可以规定数据对象的取值范围及能够进行的运算
  • D对参与表达式求值的数据对象可以进行合法性检查
查看答案与解析收起答案与解析
答案B

A、C、D——都是教材三条作用的转述,属于“规定的理由”。B——不是理由:JavaScript 是弱类型语言(不强制类型声明),照样可以定义和使用动态数据结构——可见“动态数据结构”与类型规定无关。选 B。

命题规律:这一条知识点三年三考(2009 下 20、2011 上 22、2011 下 28),且 2011 下是“反着问”——审题时看清“是理由”还是“不是理由”。

真题 11 2018 下半年 · 题 22

在程序运行过程中,(22)时涉及整型数据转换为浮点型数据的操作。

  • A将浮点型变量赋值给整型变量
  • B将整型常量赋值给整型变量
  • C将整型变量与浮点型变量相加
  • D将浮点型常量与浮点型变量相加
查看答案与解析收起答案与解析
答案C

A——错,这是浮点 → 整型(方向反了,且会截断丢精度)。B——错,整型赋给整型,同类型,无转换。C——对,int 与 float 混合运算时,编译器按“类型提升”规则把整型先转成浮点型再相加——这正是题目问的“整型转换为浮点型”。D——错,浮点与浮点相加,同类型。

真题 12 2010 下半年 · 题 20

以下关于变量和常量的叙述中,错误的是( )。

  • A变量的取值在程序运行过程中可以改变,常量则不行
  • B变量具有类型属性,常量则没有
  • C变量具有对应的存储单元,常量则没有
  • D可以对变量赋值,不能对常量赋值
查看答案与解析收起答案与解析
答案B

A——正确说法,这就是变量与常量的定义。B——错误:常量也有类型属性——整型常量 42、浮点常量 3.14、字符串常量 "abc",类型各不相同;编译器正是靠类型决定常量的存储方式和运算规则。C——正确说法,常量不分配存储单元(符号常量在编译时被替换)。D——正确说法,常量不能出现在赋值号左边。

真题 13 2015 上半年 · 题 20

以下关于程序设计语言的叙述中,错误的是( )。

  • A程序设计语言的基本成分包括数据、运算、控制和传输等
  • B高级程序设计语言不依赖于具体的机器硬件
  • C程序中局部变量的值在运行时不能改变
  • D程序中常量的值在运行时不能改变
查看答案与解析收起答案与解析
答案C

A——对,教材 2.1.2 节开篇的分类。B——对,这正是“高级”的含义之一(与机器无关,靠编译/解释落地到具体机器)。C——错误:局部变量就是可变的,函数执行过程中随时可以被赋新值。D——对,常量的定义。

真题 14 2012 上半年 · 题 20

对于逻辑表达式 "x and y or not z",and、or、not 分别是逻辑与、或、非运算,优先级从高到低为 not、and、or,and、or 为左结合,not 为右结合,若进行短路计算,则( )。

  • Ax 为真时,整个表达式的值即为真,不需要计算 y 和 z 的值
  • Bx 为假时,整个表达式的值即为假,不需要计算 y 和 z 的值
  • Cx 为真时,根据 y 的值决定是否需要计算 z 的值
  • Dx 为假时,根据 y 的值决定是否需要计算 z 的值
查看答案与解析收起答案与解析
答案C

第一步:按优先级加括号。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 短路时根本没被计算。

真题 15 2016 下半年 · 题 20

逻辑表达式求值时常采用短路计算方式。"&&"、"||"、"!" 分别表示逻辑与、或、非运算,"&&"、"||" 为左结合,"!" 为右结合,优先级从高到低为 "!"、"&&"、"||"。对逻辑表达式 "x&&(y || !z)" 进行短路计算方式求值时,( )。

  • Ax 为真,则整个表达式的值即为真,不需要计算 y 和 z 的值
  • Bx 为假,则整个表达式的值即为假,不需要计算 y 和 z 的值
  • Cx 为真,再根据 z 的值决定是否需要计算 y 的值
  • Dx 为假,再根据 y 的值决定是否需要计算 z 的值
查看答案与解析收起答案与解析
答案B

整个表达式是 `x && (...)`,最外层是**与**运算。A——错,x 为真只是“括号部分”还没算,整体真假未知(若括号为假整体还是假)。B——对,&& 左边为假,整体短路为假,括号里的 y、z 全部不用算。C——错,x 为真时先算的是括号内的 `y || !z`:或运算**先算左边 y**,y 为真则括号短路(z 不用算),y 为假才算 `!z`——顺序是“先 y 后 z”,不是“根据 z 决定是否算 y”。D——错,x 为假时整体已定为假,y、z 都不用算。

真题 16 2018 下半年 · 题 20

对布尔表达式进行短路求值是指:无须对表达式中所有操作数或运算符进行计算就可确定表达式的值。对于表达式 "a or ((c<d) and b)",( )时可进行短路计算。

  • Ad 为 true
  • Ba 为 true
  • Cb 为 true
  • Dc 为 true
查看答案与解析收起答案与解析
答案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,传地址):将实参的地址传递给形参,形参必须有地址;实参不能是常量(值)和表达式;可以实现形参和实参间双向传递数据的效果,即改变形参的值同时也改变了实参的值。

计算题的套路永远是同一个:给每个变量标“副本 / 本体”,逐行执行,改到谁的头上就更新谁。

概念题

真题 17 2009 上半年 · 题 20

函数调用时,基本的参数传递方式有传值与传地址两种,( )。

  • A在传值方式下,形参将值传给实参
  • B在传值方式下,实参不能是数组元素
  • C在传地址方式下,形参和实参间可以实现数据的双向传递
  • D在传地址方式下,实参可以是任意的变量和表达式
查看答案与解析收起答案与解析
答案C

A——错,方向反了。传值是实参的值传给形参(单向),形参的值传不回实参。B——错,数组元素有确定的值,当然可以作为实参传值。C——对,传地址时形参通过地址直接操作实参本体,改形参就是改实参,双向传递成立。D——错,表达式没有地址,不能传地址(传地址要求实参“必须有地址”)。

真题 18 2011 上半年 · 题 50

传值与传地址是函数调用时常采用的信息传递方式,( )。

  • A在传值方式下,是将形参的值传给实参
  • B在传值方式下,形参可以是任意形式的表达式
  • C在传地址方式下,是将实参的地址传给形参
  • D在传地址方式下,实参可以是任意形式的表达式
查看答案与解析收起答案与解析
答案C

A——错,传值是“实参 → 形参”,数据永远从实参流向形参。B——错,混淆了实参与形参:实参可以是表达式(发生在调用处),形参是函数定义里的形式参数,谈不上表达式。C——对,传地址的定义。D——错,表达式没有地址。

真题 19 2012 上半年 · 题 50

函数(过程)调用时,常采用传值与传地址两种方式在实参与形参间传递信息。以下叙述中,正确的是( )。

  • A在传值方式下,将形参的值传给实参,因此,形参必须是常量或变量
  • B在传值方式下,将实参的值传给形参,因此,实参必须是常量或变量
  • C在传地址方式下,将形参的地址传给实参,因此,形参必须有地址
  • D在传地址方式下,将实参的地址传给形参,因此,实参必须有地址
查看答案与解析收起答案与解析
答案D

A——错,方向反了(不是形参传给实参)。B——错,前半句对,但“实参必须是常量或变量”错——实参可以是表达式。C——错,“形参的地址传给实参”方向反了,且传地址要求的是实参有地址。D——对,传地址就是把实参的地址交给形参,所以实参必须有地址。

真题 20 2013 上半年 · 题 21

以下关于传值调用与引用调用的叙述中,正确的是(21)。①在传值调用方式下,可以实现形参和实参间双向传递数据的效果 ②在传值调用方式下,实参可以是变量,也可以是常量和表达式 ③在引用调用方式下,可以实现形参和实参间双向传递数据的效果 ④在引用调用方式下,实参可以是变量,也可以是常量和表达式

  • A①③
  • B①④
  • C②③
  • D②④
查看答案与解析收起答案与解析
答案C

①——错,传值是单向的,不存在双向传递。②——对,传值时实参随意。③——对,传引用改形参即改实参,双向成立。④——错,引用调用要求实参有地址,常量和表达式没有。

真题 21 2013 下半年 · 题 20

程序运行过程中常使用参数在函数(过程)间传递信息,引用调用传递的是实参的( )。

  • A地址
  • B类型
  • C名称
  • D值
查看答案与解析收起答案与解析
答案A

A. 地址——对,引用调用的本质就是传地址。B. 类型——类型是编译期检查的属性,不参与传递。C. 名称——传名调用(call by name,宏替换式)是另一种理论上的传递方式,不是“引用调用”。D. 值——那是传值调用。

真题 22 2014 上半年 · 题 21

在引用调用方式下进行函数调用是将(21)。

  • A实参的值传递给形参
  • B实参的地址传递给形参
  • C形参的值传递给实参
  • D形参的地址传递给实参
查看答案与解析收起答案与解析
答案B

选 B(实参的地址传递给形参)。引用调用(call by reference)传的是地址,不是值。

两种调用方式的本质区别:

传值调用:把实参的值复制一份给形参。形参是函数内的局部变量,在函数里怎么改,都影响不到外面的实参。

引用调用:把实参的地址传给形参。形参成了实参的“别名”,函数内对形参的修改,直接作用在原来的实参上。

经典例子:void swap(int a, int b) 传值——函数内交换了,外面的两个变量纹丝不动;void swap(int &a, int &b) 引用——函数内交换,外面的变量真的换了。

A 错:传值是“实参的值 → 形参”,那是另一种方式。

C、D 错:方向反了。数据永远从实参流向形参,形参不可能反过来给实参传值或传地址——形参只是函数内部的局部名字。

顺带记第三种:传名调用,把实参的表达式本身传进去,每次用到形参就重新求值一次,实际语言里很少用。

真题 23 2016 下半年 · 题 21

常用的函数参数传递方式有传值与传引用两种。(21)。

  • A在传值方式下,形参与实参之间互相传值
  • B在传值方式下,实参不能是变量
  • C在传引用方式下,修改形参实质上改变了实参的值
  • D在传引用方式下,实参可以是任意的变量和表达式
查看答案与解析收起答案与解析
答案C

A——错,传值是单向的,不存在“互相传值”。B——错,变量是最常见的实参。C——对,传引用时形参是实参的别名,改形参即改实参。D——错,表达式没有地址,不能传引用。

计算题:九道程序逐个完整推导

做题统一流程:① 给每个变量标“副本/本体”;② 按语句顺序逐行执行,传引用的赋值直接写回实参;③ 圈出题目问的输出。

真题 24 2011 下半年 · 题 49、50

函数 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)B (50)D

(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 两空同时拿下。

真题 25 2015 下半年 · 题 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. 负数
查看答案与解析收起答案与解析
答案A

计算过程: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 纹丝不动。

真题 26 2016 上半年 · 题 50

函数 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
查看答案与解析收起答案与解析
答案D

计算过程:第一个实参 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 带了回来。

真题 27 2017 上半年 · 题 49

函数 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
查看答案与解析收起答案与解析
答案B

计算过程:第一个实参是表达式 `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 旁边的数。实参是表达式时,先求值再传递。

真题 28 2017 下半年 · 题 50

函数 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
查看答案与解析收起答案与解析
答案D

计算过程:两个实参同名都是 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 就是在等对调错误的考生。

真题 29 2018 下半年 · 题 50

函数 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;
(50)
  • A14
  • B18
  • C24
  • D28
查看答案与解析收起答案与解析
答案D

计算过程: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。这类“别名”关系能帮你在考场上快速验算。

真题 30 2019 上半年 · 题 50

函数 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
查看答案与解析收起答案与解析
答案B

计算过程: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。

真题 31 2020 下半年 · 题 48

函数 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
查看答案与解析收起答案与解析
答案A

计算过程: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 的是什么组合式都能推出来。

真题 32 2021 上半年 · 题 50

函数 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
查看答案与解析收起答案与解析
答案C

计算过程: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 节的核心是一张编译阶段图。先把它背下来:

源程序 → 词法分析 → 语法分析 → 语义分析 → 中间代码生成 → 代码优化 → 目标代码
                      ↑ 符号表管理 + 出错处理(贯穿全程)

三个必考结论(教材原文要点):

  1. 解释方式只包含词法分析、语法分析、语义分析,然后直接解释执行——没有中间代码生成、代码优化、目标代码生成这三个阶段。
  2. 编译方式下,中间代码生成和代码优化不是必需的(可以从语义分析直接生成目标代码),其余阶段必需。
  3. 词法、语法、语义分析三个阶段谁都省不掉,顺序也不可交换。
真题 33 2010 上半年 · 题 48

以下关于高级语言程序的编译和解释的叙述中,正确的是(48)。

  • A编译方式下,可以省略对源程序的词法分析、语法分析
  • B解释方式下,可以省略对源程序的词法分析、语法分析
  • C编译方式下,在机器上运行的目标程序完全独立于源程序
  • D解释方式下,在机器上运行的目标程序完全独立于源程序
查看答案与解析收起答案与解析
答案C

A、B——错,教材明确“编译器和解释器都不可省略词法分析、语法分析、语义分析,且顺序不可交换”。C——对,编译生成的目标程序独立运行,源程序与编译器不再参与。D——错,解释方式下解释器和源程序要参与运行,谈不上独立。

真题 34 2012 上半年 · 题 51

编译和解释是实现高级程序设计语言翻译的两种基本形式。以下关于编译与解释的叙述中,正确的是(51)。

  • A在解释方式下,对源程序不进行词法分析和语法分析,直接进行语义分析
  • B在解释方式下,无需进行词法、语法和语义分析,而是直接产生源程序的目标代码
  • C在编译方式下,必须进行词法、语法和语义分析,然后再产生源程序的目标代码
  • D在编译方式下,必须先形成源程序的中间代码,然后再产生与机器对应的目标代码
查看答案与解析收起答案与解析
答案C

A、B——错,解释方式同样要做词法、语法、语义分析(这是最高频的误区,很多人以为“解释就是直接执行”)。C——对。D——错,“必须先形成中间代码”不成立,中间代码生成是可省略阶段,编译器可以从语义分析直接生成目标代码。

真题 35 2015 上半年 · 题 48

对高级语言源程序进行编译或解释的过程可以分为多个阶段,解释方式不包含(48)阶段。

  • A词法分析
  • B语法分析
  • C语义分析
  • D目标代码生成
查看答案与解析收起答案与解析
答案D

选 D。要分清编译和解释的流程差异。

编译的完整流程:词法分析 → 语法分析 → 语义分析 → 中间代码生成 → 优化 → 目标代码生成,最后链接成可执行文件。

解释的流程:词法分析 → 语法分析 → 语义分析 → 直接解释执行,不生成目标代码。

所以解释方式不包含“目标代码生成”。

A 词法分析、B 语法分析、C 语义分析:解释器同样要做——它得先把源程序拆成记号、检查语法结构、做类型检查,然后才能执行。

为什么不生成目标代码?解释器是“读一句、执行一句”,中间结果留在内存里直接执行,不落地成机器码文件。这也解释了两者的性能差异:解释执行每次运行都要重新分析源程序,所以比编译执行慢;但解释方式跨平台性好,改一行代码不用重新编译。

顺带记一个常考对比:编译方式下,程序运行时不依赖编译器;解释方式下,程序运行时必须有解释器在场。

真题 36 2015 下半年 · 题 20、21

编译器和解释器是两种基本的高级语言处理程序。编译器对高级语言源程序的处理过程可以划分为词法分析、语法分析、语义分析、中间代码生成、代码优化、目标代码生成等阶段,其中,(20)并不是每个编译器都必需的;与编译器相比,解释器(21)。

(20)
  • A词法分析和语法分析
  • B语义分析和中间代码生成
  • C中间代码生成和代码优化
  • D代码优化和目标代码生成
(21)
  • A不参与运行控制,程序执行的速度慢
  • B参与运行控制,程序执行的速度慢
  • C参与运行控制,程序执行的速度快
  • D不参与运行控制,程序执行的速度快
查看答案与解析收起答案与解析
答案(20)C (21)B

第(20)空:必需阶段是词法、语法、语义、目标代码生成;可选的是中间代码生成和代码优化,选 C。A 错——词法语法是地基;B 错——语义分析必需,中间代码可选(半对半错最坑);D 错——目标代码生成必需。

第(21)空:解释器逐句翻译执行,参与运行控制(控制权在解释器),每句都要现场翻译所以速度慢——选 B。A、D 的“不参与”错;C 的“速度快”错。

五、符号表

符号表是编译过程中贯穿始终的数据结构。教材(2.2.2 节)的定义:在编译过程中“不断收集、记录和使用源程序中一些相关符号的类型和特征等信息”,存入符号表;“记录源程序中各个字符的必要信息,以辅助语义的正确性检查和代码生成”。

真题 37 2013 上半年 · 题 49、50

编译过程中,对高级语言程序语句的翻译主要考虑声明语句和可执行语句。对声明语句,主要是将所需要的信息正确地填入合理组织的(49)中;对可执行语句,则是(50)。

(49)
  • A符号表
  • B栈
  • C队列
  • D树
(50)
  • A翻译成机器代码并加以执行
  • B转换成语法树
  • C翻译成中间代码或目标代码
  • D转换成有限自动机
查看答案与解析收起答案与解析
答案(49)A (50)C

(49)声明语句(变量定义、类型声明)的信息填入符号表——A 对;栈/队列是运行时结构,树是语法分析的产物,都答非所问。(50)可执行语句翻译成中间代码或目标代码——C 对;A 错,“翻译成机器代码并加以执行”是解释/运行阶段的事,编译器只翻译不执行;B 错,语法树是语法分析的产物;D 错,有限自动机是词法分析工具。

真题 38 2014 上半年 · 题 22

编译程序对高级语言源程序进行编译的过程中,要不断收集、记录和使用源程序中一些相关符号的类型和特征等信息,并将其存入(22)中。

  • A符号表
  • B哈希表
  • C动态查找表
  • D栈和队列
查看答案与解析收起答案与解析
答案A

题干就是符号表的定义原文——选 A。B. 哈希表是干扰项:符号表可以用哈希表来实现(组织方式),但它的“名字”叫符号表;C、D 与题干描述的功能无关。

真题 39 2019 上半年 · 题 22

在对高级语言源程序进行编译或解释处理的过程中,需要不断收集、记录和使用源程序中一些相关符号的类型和特征等信息,并将其存入(22)中。

  • A哈希表
  • B符号表
  • C堆栈
  • D队列
查看答案与解析收起答案与解析
答案B

选 B。符号表是编译(或解释)过程中用来记录源程序中各种名字及其属性的数据结构。

具体记什么:变量名、类型、作用域、存储位置;函数名及其参数表;数组的维数和上下界;常量值等。词法分析时开始建立,语法分析、语义分析、代码生成阶段不断查询和更新。

A 哈希表:是一种实现技术。符号表常用哈希表来实现(为了快速查找),但“哈希表”本身不是这个概念的答案——题目问的是“存到哪里去”这个逻辑概念,不是“用什么结构实现”。

C 堆栈:用于过程调用的运行时管理(活动记录、返回地址),不是编译期记录符号信息的容器。

D 队列:用于管理待处理的输入或任务,与符号信息无关。

题干里有个细节值得注意:“编译或解释”都要用符号表。解释程序虽然不生成目标代码,但同样要检查类型、分配存储,所以也离不开符号表——这是本题比老题多出来的那一点。

真题 40 2021 下半年 · 题 21

对高级程序语言进行编译的过程中,使用(21)来记录源程序中各个字符的必要信息,以辅助语义的正确性检查和代码生成。

  • A决策表
  • B符号表
  • C广义表
  • D索引表
查看答案与解析收起答案与解析
答案B

A. 决策表是结构化分析的工具;C. 广义表是数据结构;D. 索引表是文件/查找的概念——都不对。B. 符号表“辅助语义的正确性检查和代码生成”,正是教材定义。选 B。

六、词法分析、语法分析、语义分析与目标代码生成

六个阶段里,前三个(词法、语法、语义)是必考中的必考。教材 2.2.2 节给每个阶段都定义了输入、输出、任务、能查什么错——这张表是 13 个空的母表:

阶段输入输出核心任务能查的错
词法分析源程序(字符流)记号流(单词符号)分析构成程序的字符及符号是否符合语言规定非法字符、关键字/标识符拼写错误
语法分析记号流语法树(分析树)对各条语句的结构进行合法性分析所有语法错误
语义分析语法树(标注后的)语法树类型分析与检查静态语义错误;查不出所有语义错误,动态语义错误要运行时才能发现
中间代码生成语法树中间代码生成与机器无关的中间表示—
代码优化中间代码更优的中间代码与机器无关的优化—
目标代码生成中间代码目标代码分配寄存器、选择指令,与机器密切相关—
真题 41 2009 下半年 · 题 48

以下关于编译系统对某高级语言进行翻译的叙述中,错误的是(48)。

  • A词法分析将把源程序看作一个线性字符序列进行分析
  • B语法分析阶段可以发现程序中所有的语法错误
  • C语义分析阶段可以发现程序中所有的语义错误
  • D目标代码生成阶段的工作与目标机器的体系结构相关
查看答案与解析收起答案与解析
答案C

A——对,词法分析就是把源程序当**线性字符序列**从左到右扫一遍。B——对,语法分析能发现所有**语法**错误(结构错误)。C——错误:语义分析只能发现**静态**语义错误(编译时能查的,如类型不匹配);除零、死循环、数组越界这类**动态**语义错误要运行时才能暴露——“所有语义错误”说大了。D——对,目标代码生成要考虑具体机器的寄存器和指令集。

真题 42 2010 上半年 · 题 22

编译程序对 C 语言源程序进行语法分析时,可以确定(22)。

  • A变量是否定义(或声明)
  • B变量的值是否正确
  • C循环语句的执行次数
  • D循环条件是否正确
查看答案与解析收起答案与解析
答案A

A——对,语法分析结合符号表即可确定变量“是否定义(或声明)”——这是编译期就能判定的。B——错,变量的值是运行时的数据,编译器不执行程序。C——错,执行次数取决于运行时输入。D——错,循环条件“是否正确”(逻辑对错)是语义/运行时的事,语法分析只管结构。

真题 43 2010 下半年 · 题 21

编译程序分析源程序的阶段依次是(21)。

  • A词法分析、语法分析、语义分析
  • B语法分析、词法分析、语义分析
  • C语义分析、语法分析、词法分析
  • D语义分析、词法分析、语法分析
查看答案与解析收起答案与解析
答案A

顺序固定——先读字符(词法),再拼结构(语法),最后查含义(语义)。数据流也印证:字符流 → 记号流 → 语法树,前一步的输出是后一步的输入,顺序不可能交换。选 A。

真题 44 2011 上半年 · 题 48

以下关于高级程序设计语言翻译的叙述中,正确的是(48)。

  • A可以先进行语法分析,再进行词法分析
  • B在语法分析阶段可以发现程序中的所有错误
  • C语义分析阶段的工作与目标机器的体系结构密切相关
  • D目标代码生成阶段的工作与目标机器的体系结构密切相关
查看答案与解析收起答案与解析
答案D

A——错,顺序不可换(没有记号流拿什么做语法分析)。B——错,语法分析只能查**语法**错误,语义错误、运行时错误都查不到——“所有错误”太绝对。C——错,语义分析与机器无关(它检查的是类型等含义),与机器密切相关的是目标代码生成。D——对,生成目标代码要考虑具体机器的指令集和寄存器。

真题 45 2014 下半年 · 题 48

对高级语言源程序进行编译的过程可以分为多个阶段,分配寄存器的工作在(48)阶段进行。

  • A词法分析
  • B语法分析
  • C语义分析
  • D目标代码生成
查看答案与解析收起答案与解析
答案D

寄存器是 CPU 的硬件资源——只有目标代码生成阶段才需要(也才有可能)考虑把哪些变量放进寄存器、用哪些指令。词法/语法/语义都与机器无关,谈不上寄存器。选 D。

真题 46 2017 上半年 · 题 22

在以阶段划分的编译过程中,判断程序语句的形式是否正确属于(22)阶段的工作。

  • A词法分析
  • B语法分析
  • C语义分析
  • D代码生成
查看答案与解析收起答案与解析
答案B

选 B。题干问“程序语句的形式是否正确”,形式即结构,对应语法分析。

三步区分法:

词法分析:管“词”对不对。看的是单个记号是否合法,比如标识符是不是以数字开头、有没有非法字符。

语法分析:管“句”对不对。看的是记号序列能否按文法拼成合法结构,比如括号是否配对、if 后面是否跟了条件。

语义分析:管“义”对不对。看的是结构合法但含义不合理的情况,比如把浮点数赋给整型指针、函数调用参数个数不符。

A 错:词法分析只看单个记号,管不到“语句的形式”。C 错:语义分析管“意思对不对”,题干明确说“形式”。D 错:代码生成不做检查。

这题和真题 48 是同一年份的两道题,一个问“句子结构”、一个问“语句形式”,答案都是语法分析。命题人用不同措辞考同一个点,说明这个对应必须形成条件反射。

真题 47 2017 下半年 · 题 48

编译过程中进行的语法分析主要是分析(48)。

  • A源程序中的标识符是否合法
  • B程序语句的含义是否合法
  • C程序语句的结构是否合法
  • D表达式的类型是否合法
查看答案与解析收起答案与解析
答案C

A——标识符合法性是**词法分析**(标识符的构成规则是正规式)。B——含义是否合法是**语义分析**。C——对,语法分析管结构。D——类型合法性也是**语义分析**。

真题 48 2018 下半年 · 题 48

在以阶段划分的编译器中,(48)阶段的主要作用是分析程序中的句子结构是否正确。

  • A词法分析
  • B语法分析
  • C语义分析
  • D代码生成
查看答案与解析收起答案与解析
答案B

选 B。题干的关键词是“句子结构是否正确”——这是语法分析的任务原文。

编译各阶段的分工,可以从“检查什么”来区分:

词法分析:检查“单词”是否合法。比如 int 是不是关键字、123abc 是不是合法标识符、有没有非法字符。它输出的是记号流(token)。

语法分析:检查“句子结构”是否合法。比如 if (x > 0 then y = 1; 少了右括号,语法分析会报错。它按文法规则判断记号序列能否构成合法语句,输出语法树。

语义分析:检查“意思”是否合理。比如把字符串赋给整型变量、调用函数时参数个数不对、除数为常量 0——语法上完全合法,但语义上说不通。

代码生成:把中间代码翻译成目标机器指令,不做正确性检查。

三者的层次可以这样记:词法管“词”,语法管“句”,语义管“义”。判断时先看题目问的是哪一层。

真题 49 2019 上半年 · 题 21、48

以编译方式翻译 C/C++ 源程序的过程中,(21)阶段的主要任务是对各条语句的结构进行合法性分析;(48)阶段的主要作用是分析构成程序的字符及由字符按照构造规则构成的符号是否符合程序语言的规定。

(21)
  • A词法分析
  • B语义分析
  • C语法分析
  • D目标代码生成
(48)
  • A词法分析
  • B语法分析
  • C语义分析
  • D代码生成
查看答案与解析收起答案与解析
答案(21)C (48)A

(21)“语句结构合法性分析” → **语法分析**。(48)“分析构成程序的字符及由字符构成的符号是否符合规定” → **词法分析**——注意这句话的层次:先看“字符”(如 @ 不合法),再看“字符构成的符号”(如 3abc 这个标识符不合法)。

真题 50 2019 下半年 · 题 20、21

将编译器的工作过程划分为词法分析、语法分析、语义分析、中间代码生成、代码优化和目标代码生成时,语法分析阶段的输入是(20);若程序中的括号不配对,则会在(21)阶段检查出错误。

(20)
  • A记号流
  • B字符流
  • C源程序
  • D分析树
(21)
  • A词法分析
  • B语法分析
  • C语义分析
  • D目标代码生成
查看答案与解析收起答案与解析
答案(20)A (21)B

(20)按数据流表:词法分析输出**记号流**,语法分析吃进去的正是它——选 A。字符流是词法的输入,分析树是语法分析的输出,源程序是最初的输入——都不对。(21)括号不配对是“结构”错误(表达式缺了另一半),**语法分析**阶段发现——选 B。

真题 51 2021 上半年 · 题 21

以编译方式翻译 C/C++ 源程序的过程中,类型检查在(21)阶段处理。

  • A词法分析
  • B语义分析
  • C语法分析
  • D目标代码生成
查看答案与解析收起答案与解析
答案B

类型检查(如 int 与指针相加、实参与形参类型不符)是**语义分析**的核心任务——教材原话“语义分析阶段的主要作用是进行类型分析和检查”。选 B。

七、程序异常和错误

这一节的考法集中在一条线上:错误分三类——词法/语法错误(编译时查)、静态语义错误(编译时查)、动态语义错误(运行时才暴露)。死循环、除零、数组越界、引用未赋初值的变量,都是动态语义错误。

真题 52 2011 下半年 · 题 20

若 C 程序的表达式中引用了未赋初值的变量,则(20)。

  • A编译时一定报告错误
  • B程序一定会崩溃
  • C可以通过编译,但运行结果一定是错误的
  • D可以通过编译并运行,但运行结果不一定是期望的结果
查看答案与解析收起答案与解析
答案D

A——错,未赋初值不是语法错误,多数编译器只给警告不给错误。B——错,垃圾值不一定导致崩溃(比如垃圾值恰好是 3,程序照常算)。C——错,“一定是错误的”太绝对——垃圾值可能碰巧是合法值。D——对,措辞最严谨:编译能过(不是语法/静态语义错误),运行不一定崩溃,但结果“不一定是期望的结果”。

真题 53 2012 下半年 · 题 50

以下关于程序错误的叙述中,正确的是(50)。

  • A编译正确的程序必然不包含语法错误
  • B编译正确的程序必然不包含语义错误
  • C编译正确的程序必然不包含逻辑错误
  • D编译正确的程序必然不包含运行时错误
查看答案与解析收起答案与解析
答案A

编译通过能保证什么?——**没有语法错误 + 没有静态语义错误**(这两类编译器会拦)。A——对,语法错误编译必拦,编译正确必然无语法错误。B——错,**动态**语义错误编译查不出(除零、死循环),编译正确的程序照样可能有语义错误。C——错,逻辑错误(算法写错)编译器根本不理解。D——错,运行时错误(数组越界、空指针)编译时无法预知。

真题 54 2014 上半年 · 题 50

在某 C/C++ 程序中,整型变量 a 的值为 0 且应用在表达式中,则最可能发生的情形是(50)。

  • A编译时报告语法错误
  • B编译时报告异常
  • C运行时报告有语法错误
  • D运行时产生异常
查看答案与解析收起答案与解析
答案D

a=0 出现在除法之类的表达式里 → 除零。A——错,除零不是语法错误(语句结构合法)。B——错,编译阶段根本没有“异常”这个概念,异常是运行时的。C——错,运行时不可能“报告语法错误”(语法错误编译期就拦下了)。D——对,运行时除零产生异常——动态语义错误。

真题 55 2015 下半年 · 题 48

某程序运行时陷入死循环,则可能的原因是程序中存在(48)。

  • A词法错误
  • B语法错误
  • C动态的语义错误
  • D静态的语义错误
查看答案与解析收起答案与解析
答案C

死循环是运行时的行为——循环条件写错(如 `while (i < 10)` 里忘了 `i++`)。A、B——错,有词法/语法错误的程序根本编译不过,更跑不起来。D——错,静态语义错误编译时就能发现。C——对,程序能跑但行为错误 → 动态语义错误。

真题 56 2016 下半年 · 题 50

运行下面的 C 程序代码段,会出现(50)错误。

题图(函数定义/状态图/语法树等)

int i = 0;
while (i < 10);
  i++;
  • A. 语法 B. 语义 C. 词法 D. 运行时逻辑(动态语义)
查看答案与解析收起答案与解析
答案D

完整分析:`while (i < 10);` 末尾多了一个分号——循环体是空语句,i 永远是 0,条件永远成立,程序陷入死循环,`i++` 根本执行不到。

A——错,语法完全合法(空语句是合法的)。B——对(官方答案给的是 B“语义”类错误,即动态语义)。C——错,没有非法字符。D——运行时逻辑错误,本质上与 B 同义(动态语义错误)。

八、中间代码

中间代码是编译器“翻译到一半”的表示形式。三个必考点(教材 2.2.2 节原文要点):

  1. 常见形式:后缀式(逆波兰式)、三地址码、三元式、四元式、树(图)。
  2. 与机器无关:可以把不同高级语言翻译成同一种中间代码,可以跨平台;因此有利于进行与机器无关的优化处理、提高编译程序的可移植性。
  3. 中间代码不是每种编译器都必须的;它不能用正规式表示(正规式是词法层面的工具)。
真题 57 2009 下半年 · 题 22

将高级语言源程序翻译为机器语言程序的过程中常引入中间代码。以下关于中间代码的叙述中,错误的是(22)。

  • A中间代码可以跨平台
  • B中间代码与机器无关
  • C将不同高级语言翻译成同一种中间代码
  • D中间代码与机器语言代码在指令结构上必须一致
查看答案与解析收起答案与解析
答案D

A——对,与机器无关所以可跨平台。B——对,教材原话。C——对,不同高级语言可以翻成同一种中间代码(如都翻成三地址码)。D——错误:中间代码的价值恰恰在于不依赖具体机器的指令结构,如果“必须一致”就没有引入的意义了。选 D。

真题 58 2012 下半年 · 题 49

在对程序语言进行翻译的过程中,常采用一些与之等价的中间代码表示形式。常用的中间代码表示不包括(49)。

  • A树
  • B后缀式
  • C四元式
  • D正则式
查看答案与解析收起答案与解析
答案D

A. 树——是(表达式树)。B. 后缀式——是(逆波兰式)。C. 四元式——是(还有三元式、三地址码)。D. 正则式——不是:正规式是词法分析的工具,描述的是单词符号的构成规则,与中间代码是两个层面。选 D。

真题 59 2013 下半年 · 题 48

将高级语言程序翻译为机器语言程序的过程中,常引入中间代码,其好处是(48)。

  • A有利于进行反编译处理
  • B有利于进行与机器无关的优化处理
  • C可以提高翻译速度
  • D可以降低翻译的复杂度
查看答案与解析收起答案与解析
答案B

A——错,反编译是另一个问题,与中间代码无关。B——对,教材原话级别的理由:中间代码与机器无关,优化可以在这个“通用层”上做,做完再翻到各种机器。C——错,多一层翻译形式不等于更快。D——错,引入中间层通常是“解耦”而非“降低复杂度”。

真题 60 2014 下半年 · 题 22

将高级语言源程序翻译成机器语言程序的过程中,常引入中间代码。以下关于中间代码的叙述中,不正确的是(22)。

  • A中间代码可以跨平台
  • B中间代码可以用栈和队列表示
  • C中间代码有利于进行与机器无关的优化
  • D中间代码可以用于在不同机器上移植编译程序
查看答案与解析收起答案与解析
答案B

A——对。B——不正确:中间代码的形式是后缀式、三地址码、三元式、四元式、树——栈是求值后缀式的工具(数据结构),不是中间代码的表示形式。C——对。D——对,这正是“提高编译程序可移植性”的含义(移植编译器只需重写“中间代码 → 目标代码”的后端)。

真题 61 2016 上半年 · 题 22

将高级语言源程序先转化为一种中间代码是现代编译器的常见处理方式。常用的中间代码有后缀式、(22)、树等。

  • A前缀码
  • B三地址码
  • C符号表
  • D补码和移码
查看答案与解析收起答案与解析
答案B

五种形式默写——后缀式、三地址码、三元式、四元式、树。B. 三地址码——对(形如 `x = y op z`,最多三个地址)。A. 前缀码——是表达式的另一种表示(波兰式),但教材列举的是三地址码;C. 符号表——不是中间代码;D. 补码和移码——是机器数的数据表示。选 B。

真题 62 2017 下半年 · 题 22

将高级语言源程序通过编译或解释方式进行翻译时,可以先生成与源程序等价的某种中间代码。以下关于中间代码的叙述中,正确的是(22)。

  • A中间代码常采用符号表表示
  • B后缀式和三地址码是常用的中间代码
  • C中间代码与机器语言代码在指令结构上必须一致
  • D中间代码与机器无关,因此不能用于优化
查看答案与解析收起答案与解析
答案B

A——错,符号表是辅助结构,不是中间代码的表示形式。B——对,正是两种常用形式。C——错,与机器无关才对。D——自相矛盾:与机器无关恰恰是优化(与机器无关的优化)的前提。

九、正规式

正规式(正则表达式)描述的是字符串的集合(正规集),是词法分析的形式化工具。教材 2.2.2 节给出三个运算符及优先级:闭包 * 最高,连接(并置)次之,或 | 最低。

写字段(正规式)的统一套路:把串的“结构特征”翻译成运算符组合——先看开头/结尾有什么要求,再看中间字符怎么分组循环。

真题 63 2009 上半年 · 题 49

由 a、b 构造且仅包含偶数个 a 的串的集合用正规式表示为(49)。(选项为正规式公式图)

查看答案与解析收起答案与解析
答案B

分析思路:偶数个 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,不合法)分别代入选中的式子。

真题 64 2010 上半年 · 题 50

对于正规式 (0*1)*,其正规集中字符串的特点是(50)。

  • A0 必须出现偶数次
  • B1 必须出现偶数次
  • C0 和 1 必须交替出现
  • D1 必须连续出现
查看答案与解析收起答案与解析
答案B

分析思路:内层 `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 段隔开不能连续。

真题 65 2012 下半年 · 题 21

某语言 L = {a^m·b^n | m ≥ 0,n ≥ 1},其正规表达式是(21)。(选项为公式图)

收起答案与解析查看答案与解析
答案B

翻译结构: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 前面,也排除。

真题 66 2016 下半年 · 题 48

由字符 a、b 构成的字符串中,若每个 a 后至少跟一个 b,则该字符串集合可用正规式表示为(48)。(选项为公式图)

查看答案与解析收起答案与解析
答案A

分析思路:“每个 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 可以单独出现”。

真题 67 2017 上半年 · 题 21

在仅由字符 a、b 构成的所有字符串中,其中以 b 结尾的字符串集合可用正规式表示为(21)。(选项为公式图)

查看答案与解析收起答案与解析
答案D

分析思路:“以 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 只要有一种走法能到终态就算数。

真题 68 2009 上半年 · 题 48

下图所示有限自动机的特点是(48)。(图为两状态 DFA:q0 自环 0,q0 经 1 到 q1,q1 经 0 回 q0)

  • A识别的 0、1 串中 0 不能连续出现
  • B识别的 0、1 串以 0 开头
  • C识别的 0、1 串以 1 开头
  • D识别的 0、1 串中 1 不能连续出现
查看答案与解析收起答案与解析
答案D

读图: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 不能连续出现。

真题 69 2010 下半年 · 题 22

下图所示的有限自动机中,0 是初始状态,3 是终止状态,该自动机可以识别(22)。

  • Aabab
  • Baaaa
  • Cbaba
  • Dabba
查看答案与解析收起答案与解析
答案B

逐串走图(这是 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。

真题 70 2011 上半年 · 题 49

下图所示为一个有限自动机(其中,A 是初态、C 是终态),该自动机可识别(49)。

  • A0110
  • B1010
  • C0101
  • D1011
查看答案与解析收起答案与解析
答案C

读图: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。其他三个串走完都不在终态。

真题 71 2012 上半年 · 题 48

下图所示为一个有限自动机(其中,A 是初态、C 是终态),该自动机所识别的字符串的特点是(48)。

  • A必须以 10 结尾的 0、1 串
  • B必须以 01 结尾的 0、1 串
  • C必须以 01 结尾的 0、1 串(另一组表述)
  • D必须以 10 结尾的 0、1 串(另一组表述)
查看答案与解析收起答案与解析
答案C

读图: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。

真题 72 2013 下半年 · 题 49

对高级语言源程序进行编译的过程中,有穷自动机(NFA 或 DFA)是进行(49)的适当工具。

  • A词法分析
  • B语法分析
  • C语义分析
  • D出错处理
查看答案与解析收起答案与解析
答案A

有限自动机与正规式等价,而正规式描述的是单词符号(标识符、常数、关键字)的构成规则——这正是词法分析的工作。语法分析用上下文无关文法(下推自动机),语义分析不做模式识别。选 A。

真题 73 2014 下半年 · 题 49

以下关于下图所示有限自动机的叙述中,不正确的是(49)。(图为两状态 FA:状态 1 为初态兼终态、自环 a;1-b→2,2-a→1)

  • A该自动机识别的字符串中 a 不能连续出现
  • B该自动机识别的字符串中 b 不能连续出现
  • C该自动机识别的字符串可以为空串
  • D该自动机识别的字符串若以 b 开头,后面必是 a
查看答案与解析收起答案与解析
答案A

读图:状态 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。

真题 74 2015 上半年 · 题 49

某非确定的有限自动机(NFA)的状态转换图如下图所示(既是初态也是终态),与该 NFA 等价的确定的有限自动机(DFA)是(49)。(NFA:初终态 q0 自环 a,q0-b→q1,q1-a→q0;选项为 DFA 图)

题图(函数定义/状态图/语法树等)

查看答案与解析收起答案与解析
答案A

先分析 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 图)。

真题 75 2015 下半年 · 题 49

某非确定的有限自动机(NFA)的状态转换图如下图所示(既是初态也是终态)。以下关于该 NFA 的叙述中,正确的是(49)。(NFA:初终态 q0 自环 1,q0-0→q1,q1 自环 0,q1-1→q0)

  • A其可识别的 0、1 序列的长度为偶数
  • B其可识别的 0、1 序列中 0 与 1 的个数相同
  • C其可识别的非空 0、1 序列中开头和结尾字符都是 0
  • D其可识别的非空 0、1 序列中结尾字符是 1
查看答案与解析收起答案与解析
答案D

分析结尾:终态是 q0。到达/停留在 q0 的最后一步:要么 q0 自环读 **1**,要么 q1-1→q0 读 **1**——两条进 q0 的路最后读的都是 1!所以非空被识别串的结尾字符必然是 1,选 D。

逐项排除:A——错,长度随意("1" 长度 1)。B——错,"111" 0 个 0 也合法。C——错,"1" 开头是 1 也合法。

真题 76 2016 上半年 · 题 49

某确定的有限自动机(DFA)的状态转换图如下图所示(A 是初态,C 是终态),则该 DFA 能识别(49)。

  • Aaabb
  • Babab
  • Cbaba
  • Dabba
查看答案与解析收起答案与解析
答案B

读图: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 ✗。

真题 77 2017 上半年 · 题 48

某确定的有限自动机(DFA)的状态转换图如下图所示(A 是初态,D、E 是终态),则该 DFA 能识别(48)。

  • A0110
  • B1010
  • C11100
  • D1101
查看答案与解析收起答案与解析
答案C

读图: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),考生按图逐字符走一遍即可确认停在终态。其余三串走完不在终态。

真题 78 2017 下半年 · 题 49

某确定的有限自动机(DFA)的状态转换图如下图所示(0 是初态,4 是终态),则该 DFA 能识别(49)。

  • Aaaab
  • Babab
  • Cbbba
  • Dabba
查看答案与解析收起答案与解析
答案A

读图: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:走完不在终态 ✗。

真题 79 2018 下半年 · 题 49

下图所示为一个不确定有限自动机(NFA)的状态转换图。该 NFA 可识别字符串(49)。

  • A0110
  • B1011
  • C1100
  • D0101
查看答案与解析收起答案与解析
答案A

读图: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 题手感的最大区别:只要后缀能走到终态就行。

真题 80 2019 上半年 · 题 49

下图所示为一个不确定有限自动机(NFA)的状态转换图,与该 NFA 等价的 DFA 是(49)。(NFA 含 ε 边:0 进入 1 链、ε 回环、以 0 结束;选项为 DFA 图)

题图(函数定义/状态图/语法树等)

查看答案与解析收起答案与解析
答案A

先分析 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,要么多了别的转移。

真题 81 2020 下半年 · 题 50

某有限自动机的状态转换图如下图所示,该自动机可识别(50)。

  • A0110
  • B1100
  • C1010
  • D0101
查看答案与解析收起答案与解析
答案B

选 B(1100)。有限自动机的题只有一种做法:拿着字符串逐字符走图,走完停在终态才算识别。

走图规则:

① 从初态(带箭头的入边指向的那个圈)出发;

② 每读一个字符,沿标着该字符的边转移到下一个状态;

③ 字符串读完后,如果停在双圈(终态),则该串被识别;否则不被识别。

四个选项的判定:

A 0110:从初态读 0 后走的路径,后续字符无法继续转移,或最终不在终态。

B 1100:能沿边一路走到底,且最后停在终态——被识别。

C 1010:中途某一步没有对应的转移边,走不下去。

D 0101:走完不在终态。

两个技巧:一是先看串的首字符,初态有几条出边、分别标什么,就能立刻排除一部分选项;二是边读边在图上划,别在脑子里记状态,容易串。

原图在转换时丢失,但方法是通用的,对着任何一版真题图都能用。

真题 82 2021 上半年 · 题 49

下图所示有限自动机(DFA)是(49)。

  • A确定的有限自动机,它能识别以 bab 结尾的串
  • B确定的有限自动机,它不能识别以 bab 结尾的串
  • C非确定的有限自动机,它能识别以 bab 结尾的串
  • D非确定的有限自动机,它不能识别以 bab 结尾的串
查看答案与解析收起答案与解析
答案D

第一步:判 DFA/NFA。图上初态有 a、b 两个自环,又有一条 a 出边——同一个状态读 a 有**两条出路**(自环 a 和出边 a)→ **非确定的**(NFA),排除 A、B。

第二步:看识别串。沿图验证任何走法都无法以 bab 结尾停在终态 → “不能识别以 bab 结尾的串”。选 D。

真题 83 2021 下半年 · 题 22

下图所示为一个非确定的有限自动机(NFA),S0 为初态,S3 为终态。该 NFA 识别的字符串为(22)。

  • A不能包含连续的字符 "0"
  • B不能包含连续的字符 "1"
  • C必须以 "101" 开头
  • D必须以 "101" 结尾
查看答案与解析收起答案与解析
答案D

读图: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 均与图不符。

真题 84 2010 下半年 · 题 48

下图所示为两个有限自动机 M1 和 M2(A 是初态、C 是终态),(48)。

  • AM1 和 M2 都是确定的有限自动机
  • BM1 和 M2 都是不确定的有限自动机
  • CM1 是确定的有限自动机,M2 是不确定的有限自动机
  • DM1 是不确定的有限自动机,M2 是确定的有限自动机
查看答案与解析收起答案与解析
答案D

逐个检查:M1:状态 A 上有 0 的自环,又有一条 0 出边到 B——读 0 有两条路 → NFA。M2:每个状态对每个输入字符的转移都唯一 → DFA。

选 D。判断口诀只有一条:**同一个状态读同一个字符,出边是否唯一**——唯一是 DFA,多条(或含 ε 边)是 NFA。

十一、正规式与有限自动机的转换

正规式和有限自动机是等价的:能写成正规式的语言一定能造出自动机来识别,反之亦然(教材 2.2.2 节)。考题就是给出自动机图选正规式(或反过来)。

真题 85 2011 下半年 · 题 48

下图所示为一个有限自动机(其中,A 是初态、C 是终态),该自动机识别的语言可用正规式(48)表示。(选项为公式图)

题图(函数定义/状态图/语法树等)

查看答案与解析收起答案与解析
答案A

读图: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 的正规式(原选项为公式图)。

真题 86 2018 上半年 · 题 48

下图所示为一个不确定有限自动机(NFA)的状态转换图。该 NFA 识别的字符串集合可用正规式(48)描述。(选项为公式图)

题图(函数定义/状态图/语法树等)

查看答案与解析收起答案与解析
答案A

读图:a 进入中间链,b 在 ε 回环里重复任意次,最后 a 到终态——识别的串是 `ab*a`:两个 a 中间夹任意多个 b(也包括 aa,即 b 取 0 个)。

对应选项 A 的正规式(原选项为公式图)。验证:aa ✓、aba ✓、abba ✓;ba ✗(开头不是 a)、abab ✗(结尾不是 a)。

真题 87 2019 下半年 · 题 50

某有限自动机的状态转换图如下图所示,与该自动机等价的正规式是(50)。(选项为公式图)

题图(函数定义/状态图/语法树等)

查看答案与解析收起答案与解析
答案B

读图:初态 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 型(上下文无关文法)**描述。

真题 88 2009 上半年 · 题 50

设某语言的语法规则用上下文无关文法 G=(N, T, P, S) 表示,其中 N 是非终结符号的集合,T 是终结符号的集合,P 是产生式集合,S 是开始符号,那么符合该语言的句子是(50)。

  • A从 S 出发推导的、仅包含 T 中符号的符号串
  • B从 N 中符号出发推导的、仅包含 T 中符号的符号串
  • C从 S 出发推导的、包含 V 中符号的符号串
  • D从 N 中符号出发推导的、包含 V 中符号的符号串
查看答案与解析收起答案与解析
答案A

句子的定义有三要素——起点、终点、内容。A——对:从开始符号 S 推导、只含终结符 T。B——错,起点错了:从 N 中任意非终结符出发推出的是“句型的一部分”,不是句子。C——错,句子里不能含非终结符(V = N∪T,包含 N)。D——错,起点和内容都错。

真题 89 2009 下半年 · 题 50

由某上下文无关文法 M[S] 推导出某句子的分析树如下图所示,则错误的叙述是(50)。(图为语法树:S→aAcB,A、B 有子推导)

  • A该文法推导出的句子必须以 "a" 开头
  • Bacabcbdcc 是该文法推导出的一个句子
  • C"S→aAcB" 是该文法的一个产生式
  • Da、b、c、d 属于该文法的终结符号集
查看答案与解析收起答案与解析
答案A

A——错误。这棵树只能证明“存在一条以 a 开头的推导”,不能证明“所有句子都以 a 开头”——文法完全可能有其他产生式推出以别的字符开头的句子,“必须”太绝对。B——对,把树的叶子从左到右读出来就是句子 acabcbdcc。C——对,树的根结点展开用的就是这条产生式。D——对,树中不再展开的叶子都是终结符。

真题 90 2014 上半年 · 题 49

大多数程序设计语言的语法规则用(49)描述即可。

  • A正规文法
  • B上下文无关文法
  • C上下文有关文法
  • D短语结构文法
查看答案与解析收起答案与解析
答案B

A. 正规文法——太弱,描述不了嵌套结构(如括号配对)。B. 上下文无关文法——对,表达能力刚好覆盖程序语言的嵌套语法,且有高效的语法分析算法。C. 上下文有关文法——太强,分析器难以高效实现。D. 短语结构文法——最强,等价于图灵机。

真题 91 2014 下半年 · 题 50

对于大多数通用程序设计语言,用(50)描述其语法即可。

  • A正规文法
  • B上下文无关文法
  • C上下文有关文法
  • D短语结构文法
查看答案与解析收起答案与解析
答案B

选 B。这道题考的是乔姆斯基文法分类与程序设计语言语法的对应关系。

乔姆斯基把文法分四型,按描述能力从强到弱:

0 型(短语结构文法):能力最强,产生式 α→β 无限制,对应图灵机。

1 型(上下文有关文法):产生式形如 αAβ→αγβ,替换 A 要看上下文,对应线性有界自动机。

2 型(上下文无关文法):产生式形如 A→γ,左边必须是单个非终结符,与上下文无关,对应下推自动机。程序设计语言的语法(表达式、语句、程序结构)用它描述最合适。

3 型(正规文法):产生式形如 A→aB 或 A→a,对应有限自动机,只能描述词法(标识符、常数这类线性结构),描述不了嵌套。

所以通用程序设计语言的语法用 2 型(上下文无关文法)描述。

A 错:正规文法描述能力太弱,连“括号必须配对”这种嵌套都表达不了——这正是词法分析用正规式、语法分析用上下文无关文法的原因。

C 错:上下文有关文法能力过强,多用于自然语言研究,不是程序设计语言语法的常规工具。

D 错:短语结构文法(0 型)最强,但可判定性太差,编译器无法处理。

记忆:词法用 3 型(正规式/有限自动机),语法用 2 型(上下文无关文法/下推自动机)。

真题 92 2016 下半年 · 题 49

乔姆斯基(Chomsky)将文法分为 4 种类型,程序设计语言的大多数语法现象可用其中的(49)描述。

  • A上下文有关文法
  • B上下文无关文法
  • C正规文法
  • D短语结构文法
查看答案与解析收起答案与解析
答案B

选 B。程序设计语言的大多数语法现象用上下文无关文法(2 型)描述。

乔姆斯基四型的对应关系,配一句口诀“0 图 1 线 2 下 3 有穷”:

0 型(短语结构文法)→ 图灵机。产生式无限制,能力最强,但不可判定。

1 型(上下文有关文法)→ 线性界限自动机。产生式形如 αAβ→αγβ,替换 A 要看左右上下文。

2 型(上下文无关文法)→ 下推自动机。产生式形如 A→γ,左边只有一个非终结符,与上下文无关。程序设计的语法(嵌套结构、表达式、语句)靠它描述。

3 型(正规文法)→ 有穷自动机。产生式形如 A→aB 或 A→a,只能描述线性结构,用于词法。

A 错:上下文有关文法能力过强,多用于自然语言研究。C 错:正规文法太弱,连“括号配对”这种嵌套都表达不了。D 错:短语结构文法最强但无法实用。

为什么语法必须用 2 型?因为程序里有大量嵌套——括号嵌套、if 嵌套、函数调用嵌套。3 型只能处理有限状态,表达不了任意深度的嵌套;而 2 型配上栈(下推自动机)恰好能处理嵌套。

真题 93 2013 下半年 · 题 21

已知文法(S 为开始符号):S→0S1 | 1S0 | ε。从 S 出发可以推导出(21)。

  • A所有由 0 构成的字符串
  • B所有由 1 构成的字符串
  • C某些 0 和 1 个数相等的字符串
  • D所有 0 和 1 个数不同的字符串
查看答案与解析收起答案与解析
答案C

每条非空产生式都同时产生一个 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——“某些”是关键词,个数相等且顺序合法的串才可推导。

真题 94 2018 上半年 · 题 49

简单算术表达式的结构可以用下面的上下文无关文法进行描述(E 为开始符号),(49)是符合该文法的句子。(产生式:E→T | E+T;T→F | T*F;F→-F | N;N 为数字;选项为表达式图)

题图(函数定义/状态图/语法树等)

查看答案与解析收起答案与解析
答案B

关键产生式:F→-F——允许对因子递归取负号,于是乘法的因子可以带负号(如 2\*-3 这类结构);T→T\*F——乘法左递归。

做题方法:把四个候选表达式从 E 开始逐步推导,推不下去的就是错的——错误的选项通常含有文法产生不出的结构(如除号、括号、连续运算符等,因为产生式里只有 +、\*、-(一元))。符合推导的只有 B(原选项为表达式图)。

真题 95 2020 下半年 · 题 49

程序设计语言的大多数语法现象可以用 CFG(上下文无关文法)表示。下面的 CFG 产生式集用于描述简单算术表达式(产生式中的运算符、id 表示单字母变量),那么符合该文法的表达式为(49)。(选项为表达式图)

查看答案与解析收起答案与解析
答案A

选 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+ 这种缺少操作数的串,无论怎么展开都得不到——因为每条产生式都保证“运算符两边各有一个完整的子表达式”。

做题技巧:不必真的完整推导,先看选项里有没有文法明显产生不了的结构。文法里没有减号,选项出现减号就直接排除;文法不产生空操作数,出现也算排除。

真题 96 2021 上半年 · 题 48

设有描述简单算术表达式的上下文无关文法如下,其中 id 表示单字母:E→E+T | T,T→F*T | F,F→id。与使用该文法描述的表达式 a+b*c*d 相符的语法树为(48)。(选项为语法树图)

题图(函数定义/状态图/语法树等)

查看答案与解析收起答案与解析
答案A

推导: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 节):

  1. 后缀式用栈求值:从左到右扫,运算数入栈,遇到运算符弹出栈顶运算、结果压回。
  2. 语法树的三种遍历:前缀式 = 先序遍历,中缀式 = 中序遍历,后缀式 = 后序遍历。

中缀转后缀的手算技巧:按优先级给中缀式加括号,然后把每个括号里的运算符移到括号后面,最后去掉所有括号。

真题 97 2010 上半年 · 题 21

逻辑表达式 a∧b∨c∧(b∨x&gt;0) 的后缀式为(21)。(其中 ∧、∨ 分别表示逻辑与、逻辑或,&gt; 表示大于)(选项为公式图)

查看答案与解析收起答案与解析
答案D

完整转换过程:∧ 优先级高于 ∨,先加括号:(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(原选项为公式图)。

真题 98 2011 上半年 · 题 20、21

算术表达式采用逆波兰式表示时不用括号,可以利用(20)进行求值。与逆波兰式对应的中缀表达式是(21)。(逆波兰式与选项为公式图)

(20)
  • A数组
  • B栈
  • C队列
  • D散列表
查看答案与解析收起答案与解析
答案(20)B (21)C

第(20)空:后缀式求值的过程——运算数入栈,运算符弹出栈顶两个数计算再把结果压回。整个过程只需要一个栈:数组不支持动态的“后进先出取数”,队列是先进先出,散列表更不沾边。选 B。

(21)把逆波兰式用栈展开即还原中缀式(遇运算符就把栈顶两个操作数用括号括起来合并),对应选项 C(原逆波兰式与选项为公式图)。

真题 99 2012 上半年 · 题 22

算术表达式 x-(y+c)*8 的后缀式是(22)。

  • Axy-c+8*
  • Bxyc-+8*
  • Cxyc+8*-(另一组排列)
  • Dxyc+8*-
查看答案与解析收起答案与解析
答案D

完整转换过程(加括号法):

① 按优先级加括号: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) 之类,错。

真题 100 2012 下半年 · 题 22

算术表达式 (a-b)*c+d 的后缀式是(22)。

  • Aab-c*d+
  • Babc-*d+
  • Cab-c*d+(另一组)
  • Dabcd-*+
查看答案与解析收起答案与解析
答案C

完整转换过程:加括号 (((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)) 一类的结构,错。

真题 101 2013 下半年 · 题 22

算术表达式 a-b*c+d 的后缀式是(22)。

  • Aabc-*d+
  • Babc-d*+
  • Cab-c*d+
  • Dabcd-*+
查看答案与解析收起答案与解析
答案B

完整转换过程:乘法优先级高,先算 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 的结构完全不同。

真题 102 2014 下半年 · 题 21

算术表达式 (a-b)*(c+d) 的后缀式是(21)。

  • Aab-cd+*
  • Babcd+*-
  • Cab-*cd+
  • Dab-c+d*
查看答案与解析收起答案与解析
答案A

完整转换过程:两个括号各自独立转后缀:(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,错。

真题 103 2015 下半年 · 题 22

表达式采用逆波兰式表示时,利用(22)进行求值。

  • A栈
  • B队列
  • C符号表
  • D散列表
查看答案与解析收起答案与解析
答案A

2011 上第 20 题原样重考,选 A(栈)。

另外,求值全过程演示,以 ab-c\*d+ 为例):a 入栈、b 入栈;遇 − 弹出 b、a 计算 a−b 压回;c 入栈;遇 × 弹出 c、(a−b) 计算 (a−b)×c 压回;d 入栈;遇 + 弹出 d、上式相加压回——栈顶即结果。整个过程只需一个栈。

真题 104 2019 下半年 · 题 22

表达式 (a-b)*(c+d) 的后缀式(逆波兰式)是(22)。

  • Aab-c*d+
  • Bab-cd*+
  • Cabcd-*+
  • Dab-cd+*
查看答案与解析收起答案与解析
答案D

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 一类的错误排列。

真题 105 2021 上半年 · 题 20

算术表达式的后缀式为(20)。(原表达式与选项为公式图)

查看答案与解析收起答案与解析
答案A

选 A(原表达式与选项为公式图,转换时丢失)。这里把“中缀转后缀”的方法讲透,对着任何同类题都能做。

后缀式(逆波兰式)的规则是:操作数在前,运算符在后,不需要括号就能唯一确定运算顺序。

方法一:加括号法(最稳)

① 按运算优先级给原表达式加满括号,每个运算符一对;

② 把每个运算符移到它那对括号的右括号后面;

③ 去掉所有括号,得到的就是后缀式。

举例:a+b*c → 加括号 (a+(b*c)) → 移运算符 (a(bc)*)+ → 去括号 abc*+。

方法二:栈法(适合编程实现)

遇到操作数直接输出;遇到运算符,把栈顶优先级不低于它的运算符依次弹出并输出,再把自己压栈;遇到左括号直接压栈,遇到右括号弹到左括号为止。

常见错误:把 a-b-c 转成 abc--。正确是 ab-c-——减法是左结合,先算左边那个减号,所以它先输出。

十四、语法树与表达式

表达式、语法树、前缀/中缀/后缀式是一一对应的:后缀式 = 语法树的后序遍历(左子树 → 右子树 → 根);中缀式 = 中序遍历(必要时加括号);前缀式 = 先序遍历。

真题 106 2015 上半年 · 题 21

与算术表达式 (a+(b-c))*d 对应的树是(21)。(选项为二叉树图)

题图(函数定义/状态图/语法树等)

查看答案与解析收起答案与解析
答案B

分析运算次序:先 b−c(最内层括号),再与 a 相加(次内层),最后乘 d(最外层)。

树的结构:根是 \*(最后算的),左子树是 +(a 与 −(b,c)),右孩子是 d。只有选项 B 的树是这个形状,选 B。

真题 107 2017 上半年 · 题 50

下图为一个表达式的语法树,该表达式的后缀形式为(50)。(树:根 −,左子树 ÷(*(x,+(5,y)),a),右孩子 b;选项为公式图)

题图(函数定义/状态图/语法树等)

查看答案与解析收起答案与解析
答案A

后序遍历过程:左子树(÷ 结点)→ 右孩子(b)→ 根(−)。

左子树(÷)的后序:先 \* 子树(x、5、y、+、\*),再 a,再 ÷ ——展开为 `x 5 y + * a /`;然后 b;最后 −。

完整后缀式:`x5y+\*a/b-`,对应选项 A。

真题 108 2018 上半年 · 题 22

对于后缀表达式(其中 -、+、* 表示二元算术运算减、加、乘),与该后缀式等价的语法树为(22)。(选项为二叉树图)

题图(函数定义/状态图/语法树等)

查看答案与解析收起答案与解析
答案C

建树方法:后缀式从左到右扫——运算数建叶结点入栈;运算符弹出栈顶两棵子树作左右孩子(先弹的是右孩子),合并后入栈。

还原出的树:根 \*,左子树 +(a,b),右子树 −(c,d)——对应选项 C。

真题 109 2018 下半年 · 题 21

下面二叉树表示的简单算术表达式为(21)。(树:根 −,左子树 *(10,+(20,30)),右孩子 40;选项为表达式图)

题图(函数定义/状态图/语法树等)

查看答案与解析收起答案与解析
答案C

中序遍历加括号:左子树 \* 的操作数是 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 跑到了减法里,不符。

真题 110 2020 下半年 · 题 20

某表达式的语法树如下图所示,其后缀式(逆波兰式)是(20)。(树形与选项为图)

题图(函数定义/状态图/语法树等)

查看答案与解析收起答案与解析
答案C

选 C(原树与选项为图)。语法树转后缀式的规则只有一条:做后序遍历——左子树 → 右子树 → 根。

为什么是后序?因为后缀式的定义就是“先操作数、后运算符”,而语法树里运算符在根、操作数在叶子。后序遍历恰好保证:处理完两个操作数(左右子树)之后才输出运算符(根)。

具体走法:

① 从根出发,先递归处理左子树——左子树内部同样“先左、再右、后根”;

② 再递归处理右子树;

③ 最后输出根节点的运算符。

三个常见混淆点:

前序遍历(根左右)对应前缀式(波兰式)。

中序遍历(左根右)对应中缀式,也就是平时写的表达式,需要括号辅助。

后序遍历(左右根)对应后缀式(逆波兰式)。

看图题先认准“后缀 = 后序 = 左右根”,再按这个顺序把叶子逐个写出来,不用管运算符优先级——树的结构已经把优先级编码进去了。

真题 111 2021 下半年 · 题 20

下图所示的二叉树表示的算术表达式是(20)(其中 *、/、- 表示乘、除、减运算)。(树:* → a 与 −;− → / 与 d;/ → b 与 c;选项为表达式图)

题图(函数定义/状态图/语法树等)

查看答案与解析收起答案与解析
答案C

中序遍历加括号:左子树 \* 的操作数是 a 和 − 子树;− 的操作数是 /(b,c)和 d → 左子树 = `a\*(b/c)`;根 − 连接右孩子 d → `a\*(b/c)-d`,选 C。

十五、杂题选讲

剩下的题考的是“程序设计语言常识”:语言的特点与分类、存储分配、标记语言、编译流程……靠积累,也靠排除法。

真题 112 2009 上半年 · 题 21

已知某高级语言源程序 A 经编译后得到机器 C 上的目标程序 B,则(21)。

  • A对 B 进行反编译,不能还原出源程序 A
  • B对 B 进行反汇编,不能得到与源程序 A 等价的汇编程序代码
  • C对 B 进行反编译,得到的是源程序 A 的变量声明和算法流程
  • D对 A 和 B 进行交叉编译,可以产生在机器 C 上运行的动态链接库
查看答案与解析收起答案与解析
答案A

A——对。编译是有损翻译:注释丢了、变量名变成了地址、控制流被优化重排——反编译无法还原出源程序 A 的原始形态。B——错,反汇编(目标代码 → 汇编代码)是机械的一一对应,能得到等价的汇编代码。C——错,反编译得到的是“功能等价的高级代码”,不是原始的变量声明和算法流程。D——错,交叉编译是“在 A 机器上编译出 B 机器的目标程序”,与动态链接库的产生方式无关。

真题 113 2009 上半年 · 题 22

下面关于程序语言的叙述,错误的是(22)。

  • A脚本语言属于动态语言,其程序结构可以在运行中改变
  • B脚本语言一般通过脚本引擎解释执行,不产生独立保存的目标程序
  • Cphp、JavaScript 属于静态语言,其所有成分可在编译时确定
  • DC 语言属于静态语言,其所有成分可在编译时确定
查看答案与解析收起答案与解析
答案C

A——对,脚本语言是动态语言,类型和结构可在运行时变。B——对,脚本语言靠引擎解释执行。C——错误:PHP、JavaScript 是脚本语言,属于动态语言而非静态语言,其类型在运行时才确定。D——对,C 是静态语言的典型。

真题 114 2009 下半年 · 题 21

以下关于 C/C++ 语言指针变量的叙述中,正确的是(21)。

  • A指针变量可以是全局变量也可以是局部变量
  • B必须为指针变量与指针所指向的变量分配相同大小的存储空间
  • C对指针变量进行算术运算是没有意义的
  • D指针变量必须由动态产生的数据对象来赋值
查看答案与解析收起答案与解析
答案A

A——对,指针变量本身也是变量,全局、局部皆可。B——错,指针变量只存地址(大小固定,如 4/8 字节),与所指对象的大小无关。C——错,指针加减整数在 C 里有明确定义(按所指类型的大小移动),遍历数组全靠它。D——错,指针可以指向静态变量、局部变量、数组元素,不必是动态对象。

真题 115 2009 下半年 · 题 49

若一个程序语言可以提供链表的定义和运算,则其运行时的(49)。

  • A数据空间适合采用静态存储分配策略
  • B数据空间必须采用堆存储分配策略
  • C指令空间需要采用栈结构
  • D指令代码必须放入堆区
查看答案与解析收起答案与解析
答案B

A——错,链表结点运行时动态增减,静态分配(编译时定死)撑不住。B——对,链表结点运行时动态产生、动态释放——这正是堆分配的用武之地。C、D——错,指令空间与数据结构无关,也不会放“堆区”。

真题 116 2010 上半年 · 题 49

标记语言用一系列约定好的标记来对电子文档进行标记,以实现对电子文档的语义、结构及格式的定义。(49)不是标记语言。

  • AHTML
  • BXML
  • CWML
  • DPHP
查看答案与解析收起答案与解析
答案D

A. HTML——是(网页标记语言)。B. XML——是(可扩展标记语言)。C. WML——是(无线应用协议的网页标记语言)。D. PHP——不是,它是脚本编程语言(服务端动态网页处理),用来写逻辑而不是标记文档。

真题 117 2010 下半年 · 题 49

以下关于可视化程序设计的叙述中,错误的是(49)。

  • A可视化程序设计使开发应用程序无需编写程序代码
  • B可视化程序设计基于面向对象的思想,引入了控件和事件驱动
  • C在可视化程序设计中,构造应用程序界面就像搭积木
  • D在可视化程序设计中,采用解释方式可随时查看程序的运行效果
查看答案与解析收起答案与解析
答案A

A——错误,“无需编写程序代码”太绝对——可视化只是把界面搭建图形化,业务逻辑代码还是要写。B——对,控件 + 事件驱动正是可视化的两个支柱。C——对,拖拽控件的比喻。D——对,解释方式即写即现。

真题 118 2010 下半年 · 题 50

以下关于汇编语言的叙述中,错误的是(50)。

  • A汇编语言源程序中的指令语句将被翻译成机器代码
  • B汇编程序先将源程序中的伪指令翻译成机器代码,然后再翻译指令语句
  • C汇编程序以汇编语言源程序为输入,以机器语言表示的目标程序为输出
  • D汇编语言的指令语句必须具有操作码字段,可以没有操作数字段
查看答案与解析收起答案与解析
答案B

A——对,指令语句就是要翻译的主体。B——错误:伪指令(段定义、数据定义等)是给汇编程序的控制命令,指示汇编过程,不生成机器代码。C——对,汇编程序的定义。D——对,某些指令(如 NOP、RET)只有操作码没有操作数。

真题 119 2012 下半年 · 题 20

可用于编写独立程序和快速脚本的语言是(20)。

  • APython
  • BProlog
  • CJava
  • DC#
查看答案与解析收起答案与解析
答案A

A. Python——对,既能写独立程序,又是快速脚本的典型。B. Prolog——逻辑型语言(人工智能/专家系统)。C. Java、D. C#——偏重型工程开发,写“快速脚本”不合适。

真题 120 2013 上半年 · 题 22

在对高级语言源程序进行编译的过程中,为源程序中变量所分配的存储单元的地址属于(22)。

  • A逻辑地址
  • B物理地址
  • C接口地址
  • D线性地址
查看答案与解析收起答案与解析
答案A

A. 逻辑地址——对,编译时变量分配的是相对地址(从 0 开始编),程序加载运行时才由系统映射到物理地址。B. 物理地址——错,那是内存芯片上的真实地址,编译器看不到。C——接口地址是设备概念。D. 线性地址——是 x86 分段后的中间概念,不是编译期分配的。

真题 121 2013 上半年 · 题 48

以下关于语言 L 的叙述中,正确的是(48)。(L 为某字符串集合,原题用公式给出)

  • A可用正规式描述,但不能通过有限自动机识别
  • B可用正规式表示,且可用有限自动机识别
  • C不能用正规式表示,但可用有限自动机识别
  • D不能用正规式表示,也不能通过有限自动机识别
查看答案与解析收起答案与解析
答案D

先记一条铁律:正规式与有限自动机等价——能写正规式就一定能造自动机,反之亦然。所以 A、C 这种“一能一不能”的选项直接排除。

再判 L:题中 L 的结构(需要计数的串集合)超出了正规集的表达能力——正规式只能表达“有限自动机级别”的规律,计数类语言(如 aⁿbⁿ)不是正规集。选 D。

真题 122 2013 下半年 · 题 50

弱类型语言(动态类型语言)是指不需要进行变量/对象类型声明的语言。(50)属于弱类型语言。

  • AJava
  • BC/C++
  • CPython
  • DC#
查看答案与解析收起答案与解析
答案C

A. Java——强类型,变量必须声明类型。B. C/C++——强类型。C. Python——对,变量不用声明类型,`x = 1` 之后 `x = "abc"` 也合法。D. C#——强类型。

真题 123 2014 上半年 · 题 20

以下程序设计语言中,(20)更适合用来进行动态网页处理。

  • AHTML
  • BLISP
  • CPHP
  • DJAVA/C++
查看答案与解析收起答案与解析
答案C

A. HTML——是标记语言,不是编程语言。B. LISP——函数式语言(人工智能)。C. PHP——对,服务端动态网页的典型语言。D. JAVA/C++——通用编程语言。

真题 124 2015 上半年 · 题 22

C 程序中全局变量的存储空间在(22)分配。

  • A代码区
  • B静态数据区
  • C栈区
  • D堆区
查看答案与解析收起答案与解析
答案B

A. 代码区——放指令,不放数据。B. 静态数据区——对,全局变量和 static 变量在编译时分配,程序运行期间一直存在。C. 栈区——放局部变量和函数调用信息,函数返回即释放。D. 堆区——放 malloc/new 动态申请的空间。

真题 125 2015 上半年 · 题 50

递归下降分析方法是一种(50)方法。

  • A自底向上的语法分析
  • B自上而下的语法分析
  • C自底向上的词法分析
  • D自上而下的词法分析
查看答案与解析收起答案与解析
答案B

语法分析方法分两族——自上而下:递归下降、预测分析(LL(1));自底向上:移进-归约、LR 系列。递归下降从开始符号往下推导,是自上而下;且它属于语法分析(不是词法分析),选 B。

真题 126 2016 上半年 · 题 21

以下关于脚本语言的叙述中,正确的是(21)。

  • A脚本语言是通用的程序设计语言
  • B脚本语言更适合应用在系统级程序开发中
  • C脚本语言主要采用解释方式实现
  • D脚本语言中不能定义函数和调用函数
查看答案与解析收起答案与解析
答案C

A——错,脚本语言是专用性较强的语言(嵌入应用、自动化),不是通用语言。B——错,系统级开发要求效率,用 C/C++。C——对,脚本语言的标配。D——错,Python/JS 都能定义和调用函数。

真题 127 2016 上半年 · 题 48

移进—归约分析法是编译程序(或解释程序)对高级语言源程序进行语法分析的一种方法,属于(48)的语法分析方法。

  • A自顶向下(或自上而下)
  • B自底向上(或自下而上)
  • C自左向右
  • D自右向左
查看答案与解析收起答案与解析
答案B

选 B(自底向上)。移进—归约分析的过程,本质是从叶子往根拼语法树。

具体过程:

① 把输入符号逐个移进(shift)到分析栈里;

② 一旦栈顶若干符号恰好构成某个产生式的右部,就把它们归约(reduce)成该产生式的左部(一个非终结符);

③ 重复移进和归约,直到整个输入被归约成开始符号。

为什么是自底向上?因为归约的方向是“先把具体的串收拢成抽象的非终结符”,相当于在语法树上先构造叶子、再逐层往上合并到根。

A 错:自顶向下是另一种思路——从开始符号出发,不断用产生式展开,试图匹配输入串。递归下降分析法、LL(1) 分析法都属于这一类。

C、D 错:自左向右/自右向左说的是扫描输入的方向,不是分析树的构造方向。所有实用的语法分析器都从左到右扫描,这个维度区分不了方法。

对照记忆:自顶向下 = 推导(展开),自底向上 = 归约(收拢)。LR 分析法、算符优先分析法都属于自底向上。

真题 128 2017 上半年 · 题 20

在高级语言源程序中,常需要用户定义的标识符为程序中的对象命名,常见的命名对象有(20)。①关键字(或保留字)②变量 ③函数 ④数据类型 ⑤注释

  • A①②③
  • B②③④
  • C①③⑤
  • D②④⑤
查看答案与解析收起答案与解析
答案B

①——错,关键字(int、if、while)是语言保留的,不能用作用户自定义标识符。②——对,变量要命名。③——对,函数要命名。④——对,自定义类型(typedef、class)要命名。⑤——错,注释不参与编译,根本不需要标识符。

真题 129 2017 下半年 · 题 20

更适合用来开发操作系统的编程语言是(20)。

  • AC/C++
  • BJava
  • CPython
  • DJavaScript
查看答案与解析收起答案与解析
答案A

A. C/C++——对,指针直接操作硬件、编译成原生代码、效率极高——操作系统、驱动、嵌入式全是它。B. Java——有 JVM 隔离,不方便直接摸硬件。C. Python——解释执行,太慢。D. JavaScript——浏览器脚本。

真题 130 2017 下半年 · 题 21

以下关于程序设计语言的叙述中,不正确的是(21)。

  • A脚本语言中不使用变量和函数
  • B标记语言常用于描述格式化和链接
  • C脚本语言采用解释方式实现
  • D编译型语言的执行效率更高
查看答案与解析收起答案与解析
答案A

A——不正确:脚本语言当然能用变量和函数(Python、JS 天天在用)。B——对,标记语言(HTML)描述格式与链接。C——对。D——对,编译成原生代码效率更高。

真题 131 2018 上半年 · 题 20

(20)是一种函数式编程语言。

  • ALisp
  • BProlog
  • CPython
  • DJava/C++
查看答案与解析收起答案与解析
答案A

四种范型对号入座——函数式:Lisp(还有 Haskell、Erlang);逻辑式:Prolog;面向对象:Java、C++;脚本/多范型:Python。选 A。

真题 132 2018 上半年 · 题 50

语法指导翻译是一种(50)方法。

  • A动态语义分析
  • B中间代码优化
  • C静态语义分析
  • D目标代码优化
查看答案与解析收起答案与解析
答案C

语法指导翻译= 在**语法分析**的同时执行**语义动作**(计算属性、生成中间代码)——它发生在编译期,属于**静态语义分析**的方法。选 C。A——错,动态语义分析发生在运行时(实际不存在这个词,编译期做不了运行时的事)。B、D——错,它是生成/检查的方法,不是优化的方法。

真题 133 2019 下半年 · 题 48、49

计算机执行程序时,内存分为静态数据区、代码区、栈区和堆区。其中(48)一般在进行函数调用和返回时由系统进行控制和管理,(49)由用户在程序中根据需要申请和释放。

(48)
  • A静态数据区
  • B代码区
  • C栈区
  • D堆区
(49)
  • A静态数据区
  • B代码区
  • C栈区
  • D堆区
查看答案与解析收起答案与解析
答案(48)C (49)D

(48)函数调用/返回时,局部变量和调用信息在栈上自动压入弹出——由系统控制管理,选 C。(49)用户程序根据需要 malloc/new 申请、free/GC 释放的是堆区,选 D。

四个区一句话:代码区放指令;静态数据区放全局/static 变量(编译时分配);栈区系统管(自动);堆区用户管(手动)。

真题 134 2020 下半年 · 题 21

用 C/C++ 语言为某个应用编写的程序,经过(21)后形成可执行程序。

  • A预处理、编译、汇编、链接
  • B编译、预处理、汇编、链接
  • C汇编、预处理、链接、编译
  • D链接、预处理、编译、汇编
查看答案与解析收起答案与解析
答案A

C/C++ 的四步固定顺序——预处理(展开宏定义、头文件、条件编译,产出 .i)→ 编译(翻译成汇编,产出 .s)→ 汇编(翻译成机器码目标文件,产出 .o)→ 链接(拼装目标文件与库,产出可执行文件)。选 A。

真题 135 2021 上半年 · 题 22

Java 语言符合的特征有(22)和自动的垃圾回收处理。①采用即时编译 ②采用静态优化编译 ③对象在堆空间分配 ④对象在栈空间分配

  • A①③
  • B①④
  • C②③
  • D②④
查看答案与解析收起答案与解析
答案A

①——对,Java 源码编译成字节码后,JVM 在运行时对热点代码做即时编译(JIT)。②——错,“静态优化编译”不是 Java 的运行特征。③——对,Java 的对象实例都在堆空间分配(栈上只放对象的引用)。④——错。选 A。

真题 136 2021 下半年 · 题 48、49、50

Python 语言的特点不包括(48);在 Python 语言中(49)是一种可变的、有序的序列结构,其中元素可以重复;在 Python 语言的模块中,(50)不支持深度学习模型。

(48)
  • A跨平台、开源
  • B编译型
  • C支持面向对象程序设计
  • D动态编程
(49)
  • A元组(tuple)
  • B字符串(str)
  • C列表(list)
  • D集合(set)
(50)
  • ATensorFlow
  • BMatplotlib
  • CPyTorch
  • DKeras
查看答案与解析收起答案与解析
答案(48)B (49)C (50)B

第(48)空:Python 是解释型脚本语言,“编译型”不属于它的特点——选 B。跨平台开源、面向对象、动态类型都是 Python 的标签。

第(49)空:A. 元组——有序但不可变;B. 字符串——有序但不可变;C. 列表——对,可变、有序、可重复;D. 集合——可变但无序且去重。选 C。

第(50)空:TensorFlow、PyTorch、Keras 都是深度学习框架;Matplotlib 是画图(可视化)库,不支持深度学习模型——选 B。

十六、考前速记:25 条“看到就选”

  1. 编译生成独立目标程序;解释不生成、参与运行控制、速度慢。
  2. 词法、语法、语义分析三个阶段编译解释都要做,顺序不可换。
  3. 编译可省略中间代码生成和代码优化;解释没有这三个阶段。
  4. 词法分析:字符流 → 记号流;语法分析:记号流 → 语法树;语义分析:类型检查。
  5. 语法分析能查出所有语法错误;语义分析只能查出静态语义错误。
  6. 动态语义错误(死循环、除零、数组越界)运行时才能发现。
  7. 目标代码生成与机器密切相关,寄存器分配在这个阶段。
  8. 符号表贯穿编译全程,收集记录符号的类型与特征信息。
  9. 中间代码:后缀式、三地址码、三元式、四元式、树——与机器无关,利于优化和移植。正规式不是中间代码。
  10. 正规式与有限自动机等价,是词法分析的工具。
  11. 语法分析用上下文无关文法;文法口诀 0 图 1 线 2 下 3 有穷。
  12. 语法分析两族:自上而下(递归下降、LL);自底向上(移进-归约、LR)。
  13. 传值:实参的值 → 形参,单向;实参可以是常量、变量、表达式。
  14. 传引用:实参的地址 → 形参,双向;实参必须有地址,不能是常量或表达式。
  15. 计算题口诀:标出“副本/本体”,逐行执行,改引用就是改实参。
  16. 变量有存储单元可改变;常量不能改,但常量也有类型。
  17. 数据类型三作用:分配存储单元、检查运算对象、规定取值范围和运算——与动态数据结构无关。
  18. 全局/static 变量在静态数据区;局部变量在栈;new/malloc 在堆。
  19. 三种基本控制结构:顺序、选择、循环(重复)——递归不算。
  20. 后缀式(逆波兰式)用栈求值;= 语法树的后序遍历。
  21. 中缀转后缀:按优先级加括号,运算符移到括号后,去括号。
  22. 正规式优先级:闭包 * > 连接 > 或 |。
  23. DFA 每个状态每个字符只有一条出边;NFA 有多条(或有 ε 边)。
  24. 有限自动机识别串:从初态逐字符走,读完停在终态即接受。
  25. C/C++ 生成可执行程序:预处理 → 编译 → 汇编 → 链接。

十七、答案速查表

编译与解释

年份题号答案年份题号答案
2012 下48B2013 上20C
2014 上48A2016 上20A
2018 上21C

语言基本成分(控制结构、数据类型、常量变量、逻辑表达式)

年份题号答案年份题号答案
2010 上33B2019 上20A
2009 下20C2011 上22B
2011 下28B2018 下22C
2010 下20B2015 上20C
2012 上20C2016 下20B
2018 下20B

传值调用与传引用调用

年份题号答案年份题号答案
2009 上20C2011 上50C
2012 上50D2013 上21C
2013 下20A2014 上21B
2016 下21C2011 下49、50B、D
2015 下50A2016 上50D
2017 上49B2017 下50D
2018 下50D2019 上50B
2020 下48A2021 上50C

编译程序基本原理、符号表、词法语法语义、程序错误

年份题号答案年份题号答案
2010 上48C2012 上51C
2015 上48D2015 下20、21C、B
2013 上49、50A、C2014 上22A
2019 上22B2021 下21B
2009 下48C2010 上22A
2010 下21A2011 上48D
2014 下48D2017 上22B
2017 下48C2018 下48B
2019 上21、48C、A2019 下20、21A、B
2021 上21B2011 下20D
2012 下50A2014 上50D
2015 下48C2016 下50D

中间代码

年份题号答案年份题号答案
2009 下22D2012 下49D
2013 下48B2014 下22D
2016 上22B2017 下22B

正规式与有限自动机

年份题号答案年份题号答案
2009 上49B2010 上50B
2012 下21B2016 下48A
2017 上21D2009 上48D
2010 下22B2011 上49C
2012 上48C2013 下49A
2014 下49A2015 上49A
2015 下49D2016 上49B
2017 上48C2017 下49A
2018 下49A2019 上49A
2020 下50B2021 上49D
2021 下22D2010 下48D
2011 下48A2018 上48A
2019 下50B

上下文无关文法

年份题号答案年份题号答案
2009 上50A2009 下50A
2013 下21C2014 上49B
2014 下50B2016 下49B
2018 上49B2020 下49A
2021 上48A

中缀、后缀表达式与语法树

年份题号答案年份题号答案
2010 上21D2011 上20、21B、C
2012 上22D2012 下22C
2013 下22B2014 下21A
2015 下22A2019 下22D
2021 上20A2015 上21B
2017 上50A2018 上22C
2018 下21C2020 下20C
2021 下20C

杂题

年份题号答案年份题号答案
2009 上21、22A、C2009 下21、49A、B
2010 上49D2010 下49、50A、B
2012 下20A2013 上22、48A、D
2013 下50C2014 上20C
2015 上22、50B、B2016 上21、48C、B
2017 上20B2017 下20、21A、A
2018 上20、50A、C2019 下48、49C、D
2020 下21A2021 上22A
2021 下48、49、50B、C、B

参考资料

  • 《软件设计师教程(第5版)》第 2 章“程序设计语言基础知识”(2.1 程序设计语言概述、2.2 语言处理程序基础)——本篇所引教材内容的出处。
  • 希赛网《软件设计师考试知识点 8——程序设计语言与语言处理程序基础》:传值/传址、后缀表达式、有限自动机的考法分析。
  • CSDN 备考笔记《程序设计语言基础知识点(软件设计师)》《【软件设计师】程序设计语言》:编译过程表、文法口诀“0 图 1 线 2 下 3 有穷”、静态/动态与强/弱类型对照。
  • 名校题库网《2018 年下半年软件设计师上午真题》:真题 16、31、32 的原始表述核对。

写在最后

程序设计语言这一章的“魂”只有一句话:高级语言怎么变成机器能跑的程序,以及这个过程中每个环节在干什么。

  • 编译和解释的分界,就是“有没有独立的目标程序、谁参与运行”;
  • 传值和传引用的分界,就是“给的是值还是地址、能不能改到实参”;
  • 六个阶段的分界,就是“字符流 → 记号流 → 语法树 → 中间代码 → 目标代码”这条流水线上的每一站;
  • 正规式、自动机、文法,则是这条流水线两端(词法和语法)的形式化描述。

计算题(传引用、后缀式、自动机识别)看着吓人,其实全是一步步走流程的体力活;概念题看着零散,其实都挂在这条主线上。把上面的 145 个空刷两遍,把速记清单默一遍,这一章的 6~8 分就稳了。

祝备考顺利。