编译原理龙书答案CH1


📘 第一章 1.1节 练习题中文笔记

✅ 1.1.1 编译器与解释器的区别

  • 编译器(Compiler):将源语言(源程序)一次性翻译为目标语言(通常是机器语言或汇编语言)的等价程序,同时在翻译过程中报告语法错误。
  • 解释器(Interpreter):逐条读取源程序并直接执行对应的操作,不生成独立的目标程序。

✅ 1.1.2 编译器与解释器的优缺点

a. 编译器的优点:

  • 生成的目标程序执行效率高(因为编译后是机器码,可直接运行)。

b. 解释器的优点:

  • 逐句执行,更容易进行错误诊断与调试(可精确指出出错位置)。

✅ 1.1.3 为什么有的编译器生成汇编语言而不是机器语言?

  • 汇编语言更接近人类理解的形式,更容易生成和调试;
  • 还可以利用已有的汇编器(Assembler)来进一步生成机器码,提高编译器的可移植性和模块化。

✅ 1.1.4 为什么使用 C 语言作为目标语言的好处?

  • C 编译器广泛可用,可在多种硬件平台上工作;
  • 将高级语言翻译为 C 代码,可以借助现有的 C 编译器完成后续的机器码生成,增加移植性和开发效率。

✅ 1.1.5 汇编器(Assembler)需要完成哪些任务?

  • 将汇编语言翻译成机器语言;
  • 生成可重定位的目标代码(relocatable code),便于链接器在不同内存地址上使用;
  • 处理符号表、地址分配、伪指令等。

📘 Section 1.3 练习题整理(附答案与解释)

✅ 1.3.1

问题 / Question:

指出下列术语中哪一个适用于以下编程语言。术语包括:

a. 命令式(imperative)
b. 声明式(declarative)
c. 冯·诺依曼结构(von Neumann)
d. 面向对象(object-oriented)
e. 函数式(functional)
f. 第三代语言(third-generation)
g. 第四代语言(fourth-generation)
h. 脚本语言(scripting)

适用的语言有:

  1. C
  2. C++
  3. Cobol
  4. Fortran
  5. Java
  6. Lisp
  7. ML
  8. Perl
  9. Python
  10. VB(Visual Basic)

✅ 答案 / Answer with Explanation:

✔ a. imperative(命令式)

  • 适用语言:C, C++, Fortran, Cobol, Java, VB
  • 解释:命令式语言是通过语句改变程序状态的传统编程方式。大多数早期语言(如 C、Fortran)都属于这一范式。

✔ b. declarative(声明式)

  • 适用语言:Lisp, ML
  • 解释:声明式语言侧重于“做什么”而不是“怎么做”,如函数式编程就是声明式的一种形式。

✔ c. von Neumann(冯·诺依曼结构)

  • 适用语言:C, C++, Fortran, Java, VB
  • 解释:这些语言依赖冯·诺依曼体系结构:内存中存放数据和指令,程序通过状态变化运行。

✔ d. object-oriented(面向对象)

  • 适用语言:C++, Java, Python, VB
  • 解释:这些语言支持类、继承、封装等面向对象特性。

✔ e. functional(函数式)

  • 适用语言:Lisp, ML, Python
  • 解释:函数式语言强调不可变性和函数作为一等公民。Lisp 和 ML 是经典函数式语言,Python 也支持函数式风格。

✔ f. third-generation(第三代语言)

  • 适用语言:C, C++, Fortran, Java, Cobol, VB
  • 解释:3GL 语言是结构化的高级语言,相比早期机器语言或汇编语言更接近人类语言。

✔ g. fourth-generation(第四代语言)

  • 适用语言:Cobol, VB
  • 解释:4GL 语言通常更偏向于数据库访问、报表生成等自动化开发,语法更接近自然语言。

✔ h. scripting(脚本语言)

  • 适用语言:Perl, Python
  • 解释:脚本语言通常用于自动化任务、数据处理和快速开发,解释执行、灵活易用。

总结表格:

编程语言 a. 命令式 b. 声明式 c. 冯结构 d. 面向对象 e. 函数式 f. 第三代 g. 第四代 h. 脚本
C
C++
Cobol
Fortran
Java
Lisp
ML
Perl
Python
VB

📘 Section 1.6 练习题整理(含答案与解释)


✅ 1.6.1

问题 / Question:
下面的 C 代码是块结构的,请指出最终 w, x, y, z 的值分别是多少。

1
2
3
4
5
6
7
8
9
10
11
12
13
int w, x, y, z;
int i = 4; int j = 5;
{
int j = 7;
i = 6;
w = i + j;
}
x = i + j;
{
int i = 8;
y = i + j;
}
z = i + j;

答案 / Answer:
w = 13, x = 11, y = 13, z = 11

解释 / Explanation:

  • i = 4, j = 5 是初始定义。
  • 第一块中:int j = 7; 是局部变量,i = 6; 修改了外部 iw = 6 + 7 = 13
  • 块外 x = i + j = 6 + 5 = 11
  • 第二块:int i = 8 是局部变量,使用的是局部 i。y = 8 + 5 = 13
  • 块外 z = i + j = 6 + 5 = 11

✅ 1.6.2

问题 / Question:
重复上题,适用于下列代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
int w, x, y, z;
int i = 3; int j = 4;
{
int i = 5;
w = i + j;
}
x = i + j;
{
int j = 6;
i = 7;
y = i + j;
}
z = i + j;

答案 / Answer:
w = 9, x = 7, y = 13, z = 11

解释 / Explanation:

  • 初始 i = 3, j = 4
  • 第一块中:int i = 5 是局部变量,w = 5 + 4 = 9
  • 块外:x = 3 + 4 = 7
  • 第二块中:j = 6 是局部,i = 7 修改了外部 iy = 7 + 6 = 13
  • 块外 z = i + j = 7 + 4 = 11(此时外部 j 仍为 4)

✅ 1.6.3

问题 / Question:
假设使用静态作用域(static scoping),给出图 1.14 中每一个声明的作用域(scope)。图略(假设图中有 5 个嵌套块:B1 到 B5)。

答案 / Answer:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Block B1:
w -> B1, B3, B4
x -> B1, B2, B4
y -> B1, B5
z -> B1, B2, B5

Block B2:
x -> B2, B3
z -> B2

Block B3:
w -> B3
x -> B3

Block B4:
w -> B4
x -> B4

Block B5:
y -> B5
z -> B5

解释 / Explanation:

  • 静态作用域是从声明位置向内传播的,也就是说:如果当前块中没有声明变量,则向外层寻找;
  • 同一个变量名在内层声明会**遮蔽(shadow)**外层的同名变量。

✅ 1.6.4

问题 / Question:
下列 C 程序会输出什么?

1
2
3
4
5
#define a (x + 1)
int x = 2;
void b() { x = a; printf("%d\n", x); }
void c() { int x = 1; printf("%d\n", a); }
void main () { b(); c(); }

答案 / Answer:
输出:

1
2
3  
2

解释 / Explanation:

  • #define a (x + 1) 是预处理宏定义,会直接文本替换
  • b() 中使用的是全局变量 x = 2,所以 a 变成 (2 + 1)x = 3
  • c() 中,int x = 1 是局部变量,宏 a 展开为 (x + 1)(1 + 1) = 2