+、Q12 后缀式、Q15 解释器哪句是错的。正规式 Q13/Q14 当时对了,这页仍把算式拆开,避免再混。|=或,ab=先 a 再 b,*=前面那团 0 次或多次,()打包。字符种类=式子里出现过的不同字母/数字各算一个。* / 比 + - 紧。2024:b*(a+c)-d → bac+*d-。raise 手动抛异常;finally 有 except 也照跑;没有传统 switch。列表+列表=拼接,不是相加、也不会报错。
| 看见题干 | 闭眼选 |
|---|---|
| 解释器比编译出的机器码效率更高 | 这句错(解释更慢) |
| 词法分析依据什么 | 正规式 / 有限自动机 |
| 语法分析依据什么 | 上下文无关文法 |
| 正规集「字符种类」 | 式子里出现过的不同字符个数(ab 是 a 和 b 两个) |
(ab|c)(1|2|3) 能匹配谁 | 前半 ab 或 c,后半 1/2/3 → 如 ab2,不是 abc、不是 a2 |
b*(a+c)-d 后缀 | bac+*d-(括号里的 + 先贴在 ac 后面) |
| 改形参、实参跟着变 | 传址 / 引用传递 |
Python y = [1,2,3]+[4,5,6] | [1,2,3,4,5,6] 拼接 |
| Python 手动抛异常 | raise |
源程序是给人看的(a = b + 1)。CPU 只认机器码。中间这座翻译桥有两种走法:
| 编译 | 解释 | |
|---|---|---|
| 怎么干 | 先整份译成机器码(还常再链接成可执行文件),然后 CPU 直接跑 | 不产出完整机器码,翻译程序拿着源程序边看边执行 |
| 速度 | 跑得快(翻译只做一次) | 跑得慢(每次执行都要再译) |
| 改一行 | 常常要重新编译 | 改完立刻再跑,调试方便 |
| 典型 | C、C++ | 早期 BASIC;命令行脚本 |
还有第三条路,Java / Python / C# 常用:先编译成中间的字节码,再由虚拟机解释(或即时编译 JIT)。所以「解释器可以直接啃源程序」对,「也可以先变成字节码再解释」也对。
练一练 · 2024上 · Q15(空白卷 OCR · 你选了 B,答案是找「错误的」)
编译不是一锅炖。源程序进厂,依次过六个车间。前三个负责看懂,后三个负责生成代码。
| 步 | 车间 | 人话 | 依据 / 产物 |
|---|---|---|---|
| 1 | 词法分析 | 把字符流切成「单词」:关键字 if、名字 score、数字 95、符号 + | 依据正规式(3 型文法),用有限自动机;产出单词流 |
| 2 | 语法分析 | 检查单词能不能排成合法句子,搭出语法树。例如 if 后面该跟条件 | 依据上下文无关文法(2 型);产出语法树 / 分析树 |
| 3 | 语义分析 | 句子合法,意思对不对?类型匹不匹配、变量声明过没有 | 类型检查、符号表 |
| 4 | 中间代码生成 | 先译成一种跟具体 CPU 无关的中间表示,方便后面优化、换机器 | 三地址码等 |
| 5 | 代码优化 | 删废话、把循环算快一点,不改变程序意思 | 更短 / 更快的中间码 |
| 6 | 目标代码生成 | 变成某一种 CPU 的汇编 / 机器码 | 还要再链接上库,才是可执行文件 |
| 习惯分法 | 含哪几步 |
|---|---|
| 前端(分析部分,跟源语言有关) | 词法 + 语法 + 语义(有的书把中间代码也算前端) |
| 后端(综合部分,跟目标机器有关) | 优化 + 目标代码 |
正规式是一张「字符串长什么样」的配方。它描述的那一堆合法字符串,叫正规集。词法分析就靠它认单词。
| 写法 | 名字 | 人话 | 例子 |
|---|---|---|---|
a|b | 或 | 左边这个、或右边那个,二选一 | 1|2|3 能写出 1,或 2,或 3 |
ab | 连接(中间那一点常省略) | 先写完左边,紧接着写右边 | ab 是字母 a 后面立刻跟 b,不是「a 或 b」 |
a* | 闭包 | 前面那一团可以出现 0 次、1 次、很多次 | ab* = a、ab、abb、abbb…(b 可没有) |
(…) | 打包 | 括号里先当成一个整体,再跟外面的 | / 连接 / * 算 | (ab)* 是 ab、abab、ababab… 和 ab* 不是一回事 |
ab 是两个字符连着出现;问「字符种类」时,a 和 b 要分开数,不能把 ab 当成一种字符。这是两大段连接。前半必须先匹配完,后半再跟上,中间不能穿插。
| 段 | 式子 | 只能是这些 |
|---|---|---|
| 前半 | ab|c | 字符串 ab,或者 字符串 c。没有单独的 a,也没有 abc |
| 后半 | 1|2|3 | 字符 1 或 2 或 3 |
所以正规集里一共就这 6 个串:ab1 ab2 ab3 c1 c2 c3。
字符种类问的是:式子里出现过哪些不同的字?前半用到 a、b、c,后半用到 1、2、3 → 6 种。不是 2 种(两段),也不是 3 种(只数了数字)。
| 选项串 | 能不能被这套正规式收下 | 原因 |
|---|---|---|
ab2 | 能 | 前半 ab,后半 2 |
abc | 不能 | 前半若取 ab,后半必须是 1/2/3,不能是 c |
a2 | 不能 | 前半没有「单独一个 a」这条路 |
123 | 不能 | 前半不是 1;整个串也不在那 6 个里 |
练一练 · 2024上 · Q13、Q14(空白卷 OCR)
ab 算成一种,也别只数两段。我们平时写的 b*(a+c)-d 叫中缀:运算符夹在两个数中间。计算机更爱后缀(逆波兰):两个数写完,运算符贴在屁股后面,这样就不再需要括号。
| 写法 | 例子(b 乘 (a+c) 再减 d) |
|---|---|
| 中缀(人写的) | b * (a + c) - d |
| 后缀(机器爱的) | b a c + * d - 连写 bac+*d- |
| 前缀(运算符在最前) | - * b + a c d 上午较少单独考 |
规则就三条:
| 规则 | |
|---|---|
| 1 | 字母、数字按出现顺序原样抄到答案里,一个都不许重排成 c 在 a 前面这种 |
| 2 | 哪个运算先算完,它的运算符就早一点贴到已经抄出来的那两个操作数后面 |
| 3 | 优先级:括号里的最先;* / 比 + - 紧;同样优先级从左往右 |
拿 b*(a+c)-d 走一遍:
| 步 | 先算哪一块 | 这一块的后缀 | 整式目前变成 |
|---|---|---|---|
| ① | 括号里 a+c 最先 | a 和 c 抄下来,+ 贴后面 → ac+ | b * (ac+) - d |
| ② | 接着是乘法 b*(ac+) | b 在前,后面跟上刚才那坨,* 再贴尾 → bac+* | (bac+*) - d |
| ③ | 最后减法 | 左边整坨、右边 d,- 贴最尾 → bac+*d- | 完成 |
bac+*d-:先 ac+ 得到 (a+c);再被 b 和 * 吃掉 → b*(a+c);再和 d 做 - → b*(a+c)-d。还原回去和原式一样,就对了。bacd+*-:等于先算 c+d,再拿去和 a 乘、和 b 乘——括号已经没了,顺序全乱。选项里 bac+d*- 则是乘法一直乘到 d,少了最后那个单独的减。练一练 · 2024上 · Q12(空白卷 OCR · 你选了 B)
ac+;再乘 b → bac+*;再减 d → bac+*d-。再手热一个:(a+b)*c → ab+c*;a+b*c(没有括号,先乘)→ abc*+。这两串差一个顺序,最爱拿来挖坑。
调用函数时,外面的变量叫实参,函数括号里接住的叫形参。递法只有两种常考:
| 传值(值传递) | 传址(引用传递) | |
|---|---|---|
| 递进去的是 | 实参的复印件 | 实参的门牌号(地址) |
| 函数里改形参 | 只改复印件,外面的实参不变 | 顺着门牌号改原件,实参跟着变 |
| 形参能把结果带回来吗 | 不能(要结果得靠 return) | 能 |
| 典型 | C 里普通 int;Java 基本类型 | C 里指针 *、数组名;C++ 引用 & |
想交换两个整数 x、y:只传值,函数里怎么倒腾,外面还是原样;必须传址(或传指针),外面才会真的对调。
递归函数每次调用把自己的局部变量压进栈,一层层叠,返回再一层层弹。问「递归靠什么实现」→ 栈。
| 文法 | 别名 | 对应什么 | 编译里谁用 |
|---|---|---|---|
| 3 型 | 正规文法 | 正规式 = 有限自动机 | 词法分析 |
| 2 型 | 上下文无关文法 | 下推自动机 | 语法分析 |
| 1 型 | 上下文有关 | — | 上午很少算 |
| 0 型 | 短语结构 | 图灵机 | 最强、上午不展开 |
| NFA 不确定有限自动机 | DFA 确定有限自动机 | |
|---|---|---|
| 读一个字符 | 下一步可能有好几条路,还可以有 ε(空边) | 最多一条路,没有空边 |
| 能力 | 两者识别的语言一样(都能描述正规集),DFA 好实现 | |
正规式 → 先画 NFA → 再确定化成 DFA。上午一般只问「词法分析用有限自动机 / 正规式」,不必现场做子集构造。
语法树里:句柄 = 最左的那个直接短语(规约时先收它)。见到选项能认名字即可。
作用域两句:静态(词法)作用域 = 看变量写在哪一层(C / Java / Python);动态作用域 = 看谁调用的。多数题「C 语言」闭眼选静态。
不是让你当 Python 工程师,是认对错。
| 点 | 对的说法 | 错的说法 |
|---|---|---|
| 抛异常 | 用 raise 手动抛 | — |
| try / except / finally | finally 几乎总会执行(有 except 也跑、return 之前也跑) | 「有了 except 就不必执行 finally」 |
| except Exception | 能抓住一般错误 | 「能捕获所有异常」—— SystemExit / KeyboardInterrupt 属于 BaseException,漏在 Exception 外面 |
| 选择结构 | if / elif / else;3.10 才有 match | 「可以用 switch…case」(软考按没有传统 switch 来考) |
列表 + | [1,2,3]+[4,5,6] → 拼接成 [1,2,3,4,5,6] | 报错;或以为对应位置相加成 [5,7,9];或以为嵌成 [1,2,3,[4,5,6]](那是把一个列表 append 进去) |
练一练 · 2024上 · Q10、Q11(空白卷 OCR · 你错过)
+ 就是首尾拼接。C 是对应元素相加(NumPy 才常这样,普通 list 不是);D 是把第二个列表当成一个元素塞进去;A 不报错。2024上 Q10 Python 异常 · Q11 列表拼接 · Q12 后缀式 · Q13 正规集种类 · Q14 正规集匹配 · Q15 解释器错误项
| 或、连接、 * 闭包。(ab|c)(1|2|3) 合法串 6 个;字符种类也是 6。b*(a+c)-d = bac+*d-。raise 抛异常;finally 必跑;无 switch;list+list=拼接。