编程卓越之道(卷2):运用底层语言思想编写高级语言代码(第2版)
  • 推荐0
  • 收藏0
  • 浏览14

编程卓越之道(卷2):运用底层语言思想编写高级语言代码(第2版)

【美】Randall Hyde (作者)  张益硕 等 (译者) 张春雨 (责任编辑)

  • 书  号:9787121450747
  • 出版日期:2023-03-01
  • 页  数:700
  • 开  本:16(240*190)
  • 出版状态:上市销售
  • 原书名: Write Great Code, Volume 2: Thinking Low-Level,Writing high-Level, 2nd Edition
  • 原书号:9781593278557
  • 维护人:张春雨
《编程卓越之道(卷2):运用底层语言思想编写高级语言代码(第2版)》介绍在使用高级语言编程时,程序员如何点点滴滴地提高程序运行效率,并在编写代码时,透彻地理解变量、数组、字符串、数据结构、过程与函数等方面各种方案的优缺点,从而恰当运用。书中阐述计算机编程语言在底层硬件上的工作原理,引入了一种被称为“高级汇编语言HLA”的学习工具。通过查看、比较编译器生成的汇编语言或机器代码,程序员能够了解代码的底层实现,以便在高级语言编程时选择最恰当的方式高效地达到自身的目标。本书是一部提高程序员专业能力,以及通往编程大师之路的不可多得的佳作。
《编程卓越之道(卷2):运用底层语言思想编写高级语言代码(第2版)》适合高等学校学生在掌握基本编程能力后,在有志于从事软件行业并精于此道时修炼使用,也可供已参加工作的程序员进一步研修、优化工作技能时参考。此外,对于有意向编写编译器的程序员,此书可提供从普通应用到底层编译的衔接,便于他们学习初步的编译原理入门知识。
媲美高德纳TAOCP的程序设计领域大师经典三卷终成||卷2精华主题:免汇编高性能编程+编译器工作原理||从源程序到优雅机器码不可多得的宝藏资料
作者简介
Randall Hyde是The Art of Assembly Language(《汇编语言的编程艺术》)和Write Great Code(《编程卓越之道》) 1~3卷(均由No Starch Press出版),以及Using 6502 Assembly Language和P-Source(由Datamost出版)的作者。他也是Microsoft Macro Assembler 6.0 Bible(由Waite Group出版)一书的合著者。在过去的40年里,Hyde一直从事嵌入式软件/硬件工程师的工作,为核反应堆、交通控制系统和其他电子设备开发相关指令集。他还在加州理工大学波莫纳分校和加州大学河滨分校教授计算机科学课程。
译者简介
《编程卓越之道:运用底层语言思想编写高级语言代码》(卷2)(第2版)译著是以张益硕一家为主体,众位亲戚朋友鼎力相助下完成的。母亲刘坤玉获北京邮电大学双学士学位,现任某著名外企业务主管、高级工程师,完成了部分章节翻译及全书的文笔校核工作。父亲张菲为华中科技大学电子系硕士,资深IT人士,翻译过诸如《代码大全2》、《编程卓越之道(第2卷):运用底层语言思想编写高级语言代码》等著作,现任某IT企软件主管,负责部分章节翻译及全书的技术校核、与编辑的沟通工作。儿子张益硕积极参与并承担了本书若干章节的的翻译,现为深圳红山中学高二学生,从小对计算机、编程和英语有深厚兴趣,曾在国家奥林匹克竞赛中获奖。
前言
我们所说的“卓越代码”到底为何意?不同程序员对此有不同的观点。因而没法提供一个公认的定义让所有人都满意。本书采用以下的定义:
“卓越代码”是用一种恒定且优先顺序明确的良好软件风格写出的软件。特别是,“卓越代码”遵循一套规则,该规则指导程序员在通过源代码实现算法时做出各种决定。
然而正如我在《编程卓越之道(卷1)》(简称为WGC1)中写的那样,基本上所有人都会认可“卓越代码”有一些特性,尤其是以下几点:
高效地使用CPU,即运行速度快
高效地使用内存,即占用内存少
高效地使用系统资源
容易看懂和维护
遵守一套恒定的风格原则
采用已有软件工程习惯的明确设计
容易增强功能
经过仔细测试,健壮,即能够正常工作
有详细的说明文档
我们还能在这个清单上列出几十条内容。例如,有的程序员认为“卓越代码”必须可移植,必须遵循一整套编程风格指导,或者必须用某种语言写成,抑或一定不能用某种语言写成。有些程序员觉得“卓越代码”必须写得尽可能简洁,还有些程序员相信“卓越代码”应该很快能写出来,也有些程序员则认为“卓越代码”应该按时、在预算内实现。
卓越代码涵盖太多方面的要素,远不是一本书能够包容的。所以本书作为《编程卓越之道》系列图书的第2卷,将关注卓越代码的一个重要组成部分—性能。尽管效率并不总是软件开发努力的首要目标,也不是必须要求“质量卓越”,但人们通常都会赞同这样一种观点:效率欠佳的代码算不上卓越代码。对于现代应用程序而言,效率欠佳是主要问题之一,所以这是要强调的重要话题。
卓越代码的性能特征
随着计算机系统的速度已经从原先的兆赫级增长到几百兆赫级,又至吉赫级,软件获得了广阔的性能施展空间。时至今日,软件工程师声称“代码根本无须优化!”已是司空见惯的现象。有趣的是,使用计算机应用程序的人却很少说这样的话。
尽管本书讲述如何编写高效代码,但它并非一本关于优化的书。优化指的是在软件开发生命周期(Software Development Life Cycle,SDLC)几近结束时,由软件工程师判断其代码为何不能符合性能要求,并为达到要求而修改代码的过程。然而不幸的是,倘若直到优化阶段才想到应用程序的性能,优化将无从实施。在软件开发生命周期的前期,即设计与实现阶段,就该确保应用程序具备合理的性能特征。优化措施可以调校系统性能,但对编写糟糕的代码是无力回天的。
Tony Hoare最早说过:“不成熟的优化乃万恶之源。”而Donald Knuth让这句话流行开来。它成了长期以来有些软件工程师的战斗口号,这些工程师不到软件开发生命周期行将收尾,就总是一味地不考虑应用程序的性能问题,即便最后也往往以经济性或销售时限为借口而将优化不了了之。然而,Hoare可没说“在应用开发的早期阶段,关注应用程序的性能就是万恶之源”。他强调的是不成熟的优化,在当时这意味着注意汇编语言代码的周期数和指令条数,而不是在初始程序设计期间要操心编码的类型,毕竟此时程序的骨架尚未定型。故而Hoare的话是切中要害的。
下面这段话摘自Charles Cook在网址链接2上的一篇短文,其中谈到了某些人对上述说法断章取义的问题。
我经常在想,这句话老是导致软件设计者犯严重错误,因为不同立场的人对其有不同的解读。
这句引语的完整版本是“我们应当在97% 的时间里忘掉琐碎的效率问题:不成熟的优化乃万恶之源”。我赞同这种说法。在性能没有明显成为瓶颈时,花大量时间去精雕细刻代码是不值得的。但相反,要是在系统级设计软件,着手时就应考虑性能问题。出色的软件开发者会自觉这样做,他们已经形成了“性能问题终将导致麻烦”的意识;而没有经验的开发人员却不这么想,他们自以为后期调整一丁点儿,就能够将以前遗留的问题统统排除。
Hoare真正的意思是,软件工程师应当把心操在诸如精良的算法设计、算法的恰当实现等方面,然后再关注传统的优化措施,例如执行特定语句要花费多少CPU周期之类的问题。
我们当然可以将本书的概念用到优化阶段,但多数技术其实都需要在初始编码时运用。有着丰富经验的软件工程师大概会争辩说,这些技术都只会对性能起到微不足道的改善作用。在有些情况下,这种评价是对的。但我们必须知道,这些微小的改良措施具有累加效应。如果在程序行将完工时才付诸实施,那么这些技术很可能在软件中已无立足之地。因为在既成事实面前,履行这些思路要做的工作量太大了,何况对已经工作起来的代码做这些修改,面临的风险太大。
本书的目标
本书和卷1(WGC1)尝试填充当代程序员的教育空白,让他们能够写出高质量的代码。特别是,本书将涵盖以下概念:
对于我们的高级语言程序,为何考虑其底层的执行很重要?
编译器怎样对高级语言(HLL)语句生成机器码?
编译器如何使用底层的原始数据类型来表示高级语言数据类型?
怎样编写高级语言代码,帮助编译器生成更好的机器码?
如何利用编译器的优化机制?
怎样在编写高级语言代码时,从底层的汇编语言角度“思考”?
本书意在教授怎样选择适当的高级语言语句,以便让现代的优化型编译器生成有效率的机器码。大多数情况下,不同的高级语言语句可提供多种途径来达到给定结果;而在机器层级,有些方法天生就比另一些方法强。尽管舍弃高效的语句,选用低效的语句序列可能有充分理由—例如,出于可读性考虑—但实际情况是,多数软件工程师并不了解高级语言语句的运行开销。没有这些知识,他们在选择语句时就无法做出明智的决定。本书的目标就在于改变这种面貌。
再次说明,本书并不是讲在任何情况下都要选择最高效的语句;而在于了解不同高级语言语句的开销,从而在面临多个选项时,我们有翔实的理由来选择哪个序列是最恰当的那个。
章节组织
尽管我们不必成为汇编语言专家就能写出高效的代码,但至少得懂一些汇编语言的知识,以理解编译器的输出。第1章和第2章探讨学习汇编语言的若干方面,涵盖常见的误区、围绕编译器的考虑和有效资源等内容。第3章提供80x86汇编语言的快速入门知识。网上附录(参见链接1)给出了PowerPC、ARM、Java字节码和“通用中间语言”(Common Intermediate Language,CIL)汇编语言的启蒙教程。
在第4章和第5章中,我们将学习通过检查编译器输出来确定所用高级语言语句的代码质量。这两章描述反汇编器、目标代码转储工具、调试器、高级语言编译器显示汇编代码的各种选项,以及一些有用的软件工具。
本书的其余部分,从第6章到第15章,说明编译器为不同高级语言语句、数据类型生成机器码的原理。有了这些知识来武装头脑,我们就能选取最适当的数据类型、常量、变量和控制结构,以便生成高效的应用程序。
假设与前提条件
本书对你具备的知识有若干假定条件。如果你的个人技能匹配下列要求,就会从本书获得最大收益:
应当至少熟练掌握一种命令式(过程)语言或面向对象的编程语言,包括C与C++、Pascal、Java、Swift、BASIC、Python和汇编语言,还包括Ada、Modula-2和FORTRAN。
应当能够领会一些小问题的描述,并可完成对问题的软件解决方案从设计到实现的过程。通常半个或一个学期的大学课程,或者几个月的自行实践就能为此准备充分。
对机器组织和数据表示有基本的掌握。你应该知道十六进制、二进制编码系统,理解计算机在内存里是如何表示有符号整数、字符和字符串等高层数据类型的。接下来的几章会介绍机器语言的一些入门知识,如果你已经掌握了这些知识,就会锦上添花。倘若你感觉自己在机器组织方面的知识薄弱,可以阅读WGC1,该书完整讲解了这些内容。
本书的环境
尽管本书提供一般性知识,但我们的讨论还是有必要特定于某些系统的。由于Intel架构的个人计算机(PC)在当代最常见,因此我将用它来探讨依赖于特定系统的概念。然而,这些概念依然适用于其他系统和CPU,例如早期Power Macintosh系统上的PowerPC CPU,手机、平板电脑和单板计算机(single-board computer,SBC;例如Raspberry Pi、更高端的Arduino板)上的ARM CPU及UNIX机器上的其他精简指令集CPU;若实在没办法用其他方案写出卓越的代码时,才去执行特定于某操作系统的调用。
本书的大部分示例运行在macOS、Windows和Linux下。当创作这些示例时,只要可能,我会尽量遵从操作系统的标准库接口;只有在替代方案中只能写出“欠缺卓越”的代码时,才去执行特定于某操作系统的调用。
本书的大多数示例代码可以运行于最新Intel 架构(包括AMD在内)的CPU中,计算机的操作系统是Windows、macOS或Linux,有一定容量的RAM及其他现在个人计算机上该有的外设。这些概念倘若不是软件本身的,就也适用于Mac计算机、UNIX主机、单板计算机、嵌入式系统,甚至大型机。

目录

致谢 XXI
引言 XXIII
1 以底层语言思考,用高级语言编程 1
1.1 关于编译器质量的误区 2
1.2 最好还是学一学汇编语言 2
1.3 为何学习汇编语言并非绝对必要 3
1.4 以底层语言思考 3
1.4.1 编译器生成的机器码只会与送入的源代码质量相配 4
1.4.2 如何协助编译器生成更好的机器码 4
1.4.3 在用高级语言编程时如何以汇编语言思考 5
1.5 编程用高级语言 7
1.6 不特定于某种语言的方法 7
1.7 附加提示 8
1.8 获取更多信息 9
2 要不要学汇编语言 10
2.1 学习汇编语言的好处与障碍 11
2.2 本书如何帮助你 11
2.3 向高层汇编器求援 12
2.4 高层汇编语言或汇编器(HLA) 13
2.5 以高级语言思考,用底层语言编程 14
2.6 汇编语言的编程范型—在底层思考 15
2.7 获取更多信息 17
3 高级语言程序员应具备的80x86汇编知识 19
3.1 学一种汇编语言很好,能学几种汇编语言更好 20
3.2 80x86汇编语言的语法 20
3.2.1 80x86基本架构 21
3.2.2 寄存器 22
3.2.3 80x86的32位通用寄存器 22
3.2.4 80x86的EFLAGS寄存器 24
3.3 文字常量 25
3.3.1 二进制文字常量 25
3.3.2 十进制文字常量 26
3.3.3 十六进制文字常量 27
3.3.4 字符与字符串文字常量 28
3.3.5 浮点型文字常量 30
3.4 汇编语言中的明示(符号)常量 30
3.4.1 HLA中的明示常量 30
3.4.2 Gas中的明示常量 31
3.4.3 MASM中的明示常量 31
3.5 80x86的寻址模式 32
3.5.1 80x86的寄存器寻址模式 32
3.5.2 立即寻址模式 34
3.5.3 位移寻址模式 35
3.5.4 RIP相对寻址模式 36
3.5.5 寄存器间接寻址模式 37
3.5.6 变址寻址模式 39
3.5.7 比例变址寻址模式 42
3.6 汇编语言的数据声明 45
3.6.1 HLA的字节数据声明 45
3.6.2 MASM的字节数据声明 46
3.6.3 Gas的字节数据声明 47
3.7 在汇编语言中指定操作数尺寸 50
3.7.1 HLA的类型强制转换 50
3.7.2 MASM的类型强制转换 51
3.7.3 Gas的类型强制转换 51
3.8 获取更多信息 52
4 编译器的操作与代码生成 53
4.1 编程语言所用的文件类型 54
4.2 编程语言的源文件 54
4.2.1 源文件的记号化 54
4.2.2 专门的源文件格式 55
4.3 计算机语言处理器的类型 55
4.3.1 纯解释器 56
4.3.2 解释器 56
4.3.3 编译器 56
4.3.4 增量编译器 57
4.4 转换过程 58
4.4.1 扫描(词法分析阶段) 60
4.4.2 分析(语法分析阶段) 61
4.4.3 中间代码生成阶段 62
4.4.4 优化 63
4.4.5 编译器评测 74
4.4.6 本机码生成 74
4.5 编译器的输出 75
4.5.1 编译器输出高级语言代码 75
4.5.2 编译器输出汇编语言代码 77
4.5.3 编译器输出目标文件 78
4.5.4 编译器输出可执行文件 79
4.6 目标文件的格式 79
4.6.1 COFF文件头 80
4.6.2 COFF可选文件头 83
4.6.3 COFF区域头 86
4.6.4 COFF区域 89
4.6.5 重定位区域 90
4.6.6 调试与符号信息 90
4.7 可执行文件的格式 90
4.7.1 页、段和文件大小 91
4.7.2 内部碎片 93
4.7.3 为什么还要空间优化 94
4.8 目标文件中的数据和代码对齐 96
4.8.1 选择区域对齐值 97
4.8.2 合并区域 97
4.8.3 区域对齐的控制 98
4.8.4 库模块内的区域对齐 98
4.9 链接器及其对代码的影响 107
4.10 获取更多信息 110
5 分析编译器输出的工具 111
5.1 背景知识 112
5.2 让编译器输出汇编语言文件 113
5.2.1 GNU编译器的汇编输出 114
5.2.2 Visual C++的汇编输出 114
5.2.3 汇编语言输出示例 114
5.2.4 分析编译器的汇编输出 125
5.3 通过目标码工具分析编译器的输出 126
5.3.1 微软的dumpbin.exe工具 127
5.3.2 FSF/GNU的objdump工具 138
5.4 通过反汇编程序分析编译器的输出 145
5.5 使用Java字节码反汇编程序分析Java的输出 147
5.6 使用IL反汇编程序分析微软C#和Visual Basic的输出 150
5.7 通过调试器分析编译器的输出 152
5.7.1 使用集成开发环境带的调试器 152
5.7.2 使用独立调试器 154
5.8 比对两次编译的输出 156
5.9 获取更多信息 163
6 常量与高级语言 164
6.1 文字常量与程序效率 165
6.2 绑定时刻 169
6.3 文字常量与明示常量的比较 170
6.4 常量表达式 171
6.5 明示常量与只读内存数据的比较 173
6.6 Swift的let语句 174
6.7 枚举类型 175
6.8 布尔常量 177
6.9 浮点数常量 180
6.10 字符串常量 186
6.11 复合数据类型的常量 189
6.12 常量值不会变化 191
6.13 获取更多信息 192
7 变量 193
7.1 运行时期的内存组织 194
7.1.1 代码、常量和只读区域 195
7.1.2 静态变量区域 197
7.1.3 变量存储区域 198
7.1.4 栈区域 199
7.1.5 堆区域与动态内存分配 200
7.2 变量是什么 200
7.2.1 属性 201
7.2.2 绑定 201
7.2.3 静态数据 201
7.2.4 动态数据 201
7.2.5 作用域 202
7.2.6 生命期 202
7.2.7 变量的定义 203
7.3 变量的存储 203
7.3.1 静态绑定与静态变量 204
7.3.2 伪静态绑定和自动变量 208
7.3.3 动态绑定与动态变量 211
7.4 常见的基本数据类型 214
7.4.1 整型变量 215
7.4.2 浮点型/实数变量 219
7.4.3 字符型变量 219
7.4.4 布尔变量 220
7.5 变量地址与高级语言 221
7.5.1 对全局变量和静态变量分配存储空间 221
7.5.2 使用自动变量减小偏移量 223
7.5.3 中间变量的存储空间分配 229
7.5.4 动态变量和指针的存储空间分配 230
7.5.5 使用记录/结构减小指令偏移量 233
7.5.6 将变量保存在寄存器里 234
7.6 内存中的变量对齐 235
7.7 获取更多信息 247
8 数组 249
8.1 何谓数组 250
8.1.1 数组声明 251
8.1.2 数组在内存中的表示 256
8.1.3 Swift数组的实现 259
8.1.4 数组元素的访问 260
8.1.5 填充与打包的比较 263
8.1.6 多维数组 266
8.1.7 动态数组与静态数组的比较 285
8.2 获取更多信息 294
9 指针 295
9.1 指针的定义 296
9.2 高级语言的指针实现 297
9.3 指针与动态内存分配 300
9.4 指针操作与指针算术运算 300
9.4.1 指针与整数求和 302
9.4.2 指针与整数之差 304
9.4.3 指针与指针之差 304
9.4.4 指针比较 305
9.4.5 指针与“与/或”逻辑运算 307
9.4.6 对指针的其他操作 308
9.5 内存分配的简单示例 309
9.6 垃圾收集 311
9.7 操作系统与内存分配 312
9.8 堆内存的开销 313
9.9 常见的指针问题 315
9.9.1 所用的指针未初始化 315
9.9.2 所用的指针含有非法值 316
9.9.3 释放存储空间后仍试图继续使用 317
9.9.4 程序用过某存储空间后却不释放 318
9.9.5 使用不当的数据类型访问间接数据 319
9.9.6 进行无效指针操作 319
9.10 现代编程语言中的指针 320
9.11 托管指针 320
9.12 获取更多信息 321
10 字符串 322
10.1 字符串格式 323
10.1.1 以0结尾的字符串 324
10.1.2 带长度值前缀的字符串格式 339
10.1.3 7位字符的字符串 341
10.1.4 HLA字符串格式 343
10.1.5 基于描述记录的字符串格式 346
10.2 静态字符串、伪动态字符串和动态字符串 347
10.2.1 静态字符串 347
10.2.2 伪动态字符串 348
10.2.3 动态字符串 348
10.3 字符串的引用计数 349
10.4 Delphi字符串格式 350
10.5 在高级语言中使用字符串 350
10.6 字符串中的Unicode字符数据 352
10.6.1 Unicode字符集 353
10.6.2 Unicode码点 354
10.6.3 Unicode代码平面 354
10.6.4 代理码点 355
10.6.5 字形、字符和字形集 355
10.6.6 Unicode规范和规范的等价性 358
10.6.7 Unicode编码 359
10.6.8 Unicode组合字符 361
10.7 Unicode字符串函数和性能 363
10.8 获取更多信息 364
11 记录、联合和类 365
11.1 记录 366
11.1.1 在各种语言中声明记录 366
11.1.2 记录的例化 370
11.1.3 在编译时期初始化记录数据 377
11.1.4 记录的内存表示 383
11.1.5 使用记录改善内存效能 385
11.1.6 使用动态记录类型和数据库 387
11.2 判别式联合 388
11.2.1 在各种语言中声明联合 388
11.2.2 联合的内存表示 390
11.2.3 联合的其他用法 391
11.3 变数类型 393
11.4 命名空间 398
11.5 类与对象 401
11.5.1 类和对象的关系 401
11.5.2 C++中的简单类声明 401
11.5.3 C#和Java的类声明 404
11.5.4 Delphi中的类声明 405
11.5.5 HLA中的类声明 405
11.5.6 虚方法表 406
11.5.7 抽象方法 409
11.5.8 共享虚方法表 410
11.5.9 类的继承 410
11.5.10 类的多态 413
11.5.11 C++中的多继承 414
11.6 协议与接口 416
11.7 类、对象和性能 420
11.8 获取更多信息 421
12 算术与逻辑表达式 424
12.1 算术表达式与计算机架构 425
12.1.1 基于栈的机器 425
12.1.2 基于累加器的机器 430
12.1.3 基于寄存器的机器 432
12.1.4 算术表达式的典型形式 433
12.1.5 三地址架构 433
12.1.6 双地址架构 434
12.1.7 架构差异和写代码的关系 435
12.1.8 复杂表达式的处理 436
12.2 算术语句的优化 436
12.2.1 常量折叠 437
12.2.2 常量传播 440
12.2.3 死码消除 445
12.2.4 公共子表达式消除 451
12.2.5 强度削弱 459
12.2.6 归纳变量 464
12.2.7 循环不变体 470
12.2.8 优化器与程序员 473
12.3 算术表达式的副作用 474
12.4 包含副作用:序列点 478
12.5 避免让副作用造成麻烦 482
12.6 强制按特定顺序计算 482
12.7 短路求值 484
12.7.1 短路求值与布尔表达式 485
12.7.2 强制短路布尔求值或全面布尔求值 487
12.7.3 短路求值和全面布尔求值的效率对比 489
12.8 算术运算的相对开销 493
12.9 获取更多信息 495
13 控制结构与程序判定 497
13.1 控制结构如何影响程序效率 497
13.2 底层控制结构入门 498
13.3 goto语句 501
13.4 if语句 507
13.4.1 提高某些if/else语句的效率 510
13.4.2 强制在if语句中全面布尔求值 513
13.4.3 强制在if语句中短路布尔求值 519
13.5 switch/case语句 525
13.5.1 switch/case语句的语义 527
13.5.2 跳转表与链式比较 527
13.5.3 switch/case语句的其他实现方案 535
13.5.4 Swift的switch语句 553
13.5.5 switch语句的编译器输出 554
13.6 获取更多信息 554
14 迭代控制结构 557
14.1 while循环 557
14.1.1 在while循环中强制全面布尔求值 560
14.1.2 在while循环中强制短路布尔求值 571
14.2 repeat..until(do..until/do..while)式的循环 574
14.2.1 在repeat..until循环中强制全面布尔求值 577
14.2.2 在repeat..until循环中强制短路布尔求值 580
14.3 forever..endfor式的循环 586
14.3.1 在forever循环中强制全面布尔求值 589
14.3.2 在forever循环中强制短路布尔求值 589
14.4 定次的for循环 589
14.5 获取更多信息 591
15 函数与过程 592
15.1 简单的函数与过程调用 593
15.1.1 保存返回地址 596
15.1.2 开销的其他来由 602
15.2 叶函数/叶过程 603
15.3 宏和内联函数 607
15.4 向函数/过程传递参数 614
15.5 活动记录和栈 621
15.5.1 活动记录的拆解 624
15.5.2 对局部变量指定偏移量 628
15.5.3 对参数指定偏移量 630
15.5.4 对参数和局部变量的访问 635
15.5.5 值要保存的寄存器 647
15.5.6 Java虚拟机与微软CLR的参数、局部变量 649
15.6 参数传递机制 649
15.6.1 传递值 650
15.6.2 传递引用 650
15.7 函数返回值 652
15.8 获取更多信息 660
后记:软件工程学 663
词汇表 664
网上附录 672

读者评论