编译原理之美
宫文学
北京原点代码 CEO
46197 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 45 讲
开篇词 (1讲)
编译原理 · 期中考试周 (1讲)
编译原理之美
15
15
1.0x
00:00/00:00
登录|注册

08 | 作用域和生存期:实现块作用域和函数

尝试实现面向对象特性,探究面向对象语言的设计思想和运行期特点
面向对象语言中对象成员的作用域和生存期
探讨不同语言中作用域和生存期的特点
示例代码展示函数执行的效果
调用函数时建立栈桢和计算参数值
函数的定义和参数列表
示例代码展示块作用域的效果
实现块作用域和本地变量
栈桢的概念和模拟实现
设计数据结构区分不同变量的作用域
堆和栈的区别及内存管理机制
示例代码展示变量的生存期和内存管理机制
变量可以访问的时间段,从分配内存到收回内存的时间
不同语言中作用域的设计机制差异
示例代码展示作用域的概念和特点
计算机语言中变量、函数、类等起作用的范围
下一讲
一课一思
实现函数功能
实现块作用域
实现作用域和栈
生存期(Extent)
作用域(Scope)
作用域和生存期

该思维导图由 AI 生成,仅供参考

目前,我们已经用 Antlr 重构了脚本解释器,有了工具的帮助,我们可以实现更高级的功能,比如函数功能、面向对象功能。当然了,在这个过程中,我们还要克服一些挑战,比如:
如果要实现函数功能,要升级变量管理机制;
引入作用域机制,来保证变量的引用指向正确的变量定义;
提升变量存储机制,不能只把变量和它的值简单地扔到一个 HashMap 里,要管理它的生存期,减少对内存的占用。
本节课,我将借实现块作用域和函数功能,带你探讨作用域和生存期及其实现机制,并升级变量管理机制。那么什么是作用域和生存期,它们的重要性又体现在哪儿呢?
“作用域”和“生存期”是计算机语言中更加基础的概念,它们可以帮你深入地理解函数、块、闭包、面向对象、静态成员、本地变量和全局变量等概念。
而且一旦你深入理解,了解作用域与生存期在编译期和运行期的机制之后,就能解决在学习过程中可能遇到的一些问题,比如:
闭包的机理到底是什么?
为什么需要栈和堆两种机制来管理内存?它们的区别又是什么?
一个静态的内部类和普通的内部类有什么区别?
了解上面这些内容之后,接下来,我们来具体看看什么是作用域。

作用域(Scope)

作用域是指计算机语言中变量、函数、类等起作用的范围,我们来看一个具体的例子。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深入探讨了作用域和生存期在编程语言中的重要性,通过比较分析C、Java和JavaScript中作用域的处理机制,展示了不同语言的语义差异。文章通过具体的代码示例展示了变量的作用域大小、声明后开始的特点,以及在函数和块中声明同名变量时的覆盖情况。此外,还介绍了块作用域的概念,以及不同语言对块作用域的处理方式。文章还涉及了作用域和生存期对应到了运行时的内存管理的基本机制,包括栈和堆来做内存管理。在实现作用域和栈的部分,文章提到了设计了一个数据结构来区分不同变量的作用域,并展示了作用域的树状结构。最后,文章提到了实现块作用域的功能,让if语句和for循环语句使用块作用域和本地变量。通过本文的阅读,读者可以深入了解作用域和生存期在编程语言中的重要性,以及相关的实现机制,对于理解编程语言的内存管理和作用域规则有很大帮助。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《编译原理之美》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(26)

  • 最新
  • 精选
  • 许童童
    变量的使用范围由作用域决定,作用域由词法规则决定,词法分析生成作用域链,之后查找变量就沿着这条作用域链查找,与函数调用栈就没有关系了。一般函数的生存期就是出栈后就结束了,如果是引用对象会在本次GC中回收,如果产生了闭包,那就要等到引用闭包的变量销毁,生存期才结束。

    作者回复: 很准确,很清晰!

    2019-08-30
    27
  • 幻境之桥
    我在上图中展现了这种情况,在调用 fun 函数的时候,栈里一共有三个栈桢:全局栈桢、main() 函数栈桢和 fun() 函数栈桢,其中 main() 函数栈桢的 parentFrame 和 fun() 函数栈桢的 parentFrame 都是全局栈桢。 老师这里没有明白为什么 fun()函数栈桢的 parentFrame 是全局栈桢而不是 main()的函数栈桢 这个 parentFrame 怎么定义呢?

    作者回复: 因为fun函数和main函数都是在全局作用域中定义的。fun中如果有一个变量,不是在本地声明的,那到哪里去找呢?要到全局作用域中去找,而不是到main函数中找。 我在课程里没有介绍一个概念:词法作用域(Lexical Scope),等以后找机会加上。 词法作用域又叫做静态作用域,也就是说,程序里用到的变量,在编译时,就知道是哪个变量定义。因为完全是根据声明时的位置关系来做变量引用解析的。

    2020-04-01
    5
    16
  • Johnson
    现在课程的做法相当于AST之后直接解析执行了,所有的逻辑都堆在AST和紧接着的语义分析,没有把AST转化成IR,然后在这个IR上做各种事情,最后再到interpreter执行。是因为前期为了简单起见,所以先这么直观的来么?

    作者回复: 因为目前是在讲前端,所以就先不引入IR。 同时也是在告诉同学们,哪怕我们只拿到了AST,也已经能做很多事情了。 IR在后端部分会讲。我会给出一个自己设计的IR的例子,用IR重新实现部分功能。然后再去采用LLVM的IR。

    2019-08-30
    11
  • 吃瓜群众路人丙
    也就是说,栈里的上一级栈桢,不一定是 Scope 的父节点。 老师能举个反例吗

    作者回复: 递归函数的调用。 int a; int foo(){ a = a+1; if (a<10){ return foo(); } else{ return a; } } 在递归调用的时候,你在函数里仍然可以访问全局变量。这个全局变量不在上一级的函数栈桢里。而是在最底下那个全局变量的栈桢。

    2019-09-22
    8
  • mcuking
    其实 js 的 es6 版本已经支持块级作用域,可以用 let const 声明

    作者回复: 是的,我注意到了。 严格的表述这样写可能比较好:如果只是像java和c那样声明变量,就没有块作用域:-)

    2019-09-07
    2
    7
  • Geek_f9ea2d
    functionDeclaration : typeTypeOrVoid? IDENTIFIER formalParameters ('[' ']')* (THROWS qualifiedNameList)? functionBody 中的('[' ']')* 这个没明白什么意思,函数的声明,我觉得这样就够了:typeTypeOrVoid? IDENTIFIER formalParameters

    作者回复: 这是直接照搬的Java的语法。这是对数组的支持。目前playscript并没有支持数组,但语法也就先这么放着了。

    2019-09-11
    2
    3
  • ZYS
    宫老师,可否兼顾一下用c++的学员,介绍一下cpp版本playscript如何在visual studio2010或更高的版本运行?

    作者回复: 讲后端部分的时候,主要是用cpp版本实现的。那部分的指导资料我整理一下,写一个README.md,尽快更新到Github和码云上。 先简单说一下: 1.如果仅仅用cpp版本的Antlr,这个比较简单,你做练习的时候可以试用一下。 2.把Antlr和LLVM一起用的时候,要配置的东西更多一些,好在有cmake。

    2019-08-31
    3
  • Aaaaaaaaaaayou
    老师,playscript-java/src/main/play/DefaultFunctionType.java 中 public static boolean isType(FunctionType type1, FunctionType type2) 函数中 List<Type> paramTypes2 = type1.getParamTypes(); 是不是写错了? type1 应该改为 type2

    作者回复: 是的。谢谢你细心的阅读代码。 我已经在github里更新了,并且在commit comment中专门感谢了你:)

    2020-02-05
    2
  • 北冥Master
    牛逼,越来越深入了,看的有点吃力了

    作者回复: 后几讲涉及的都是语义功能,并涉及了一部分运行期技术(给后端技术部分提前做铺垫)。 语义上的差别是每种语言真正的差别,但底层有一些共通的机制。搞搞明白对我们学各种语言都有好处。

    2019-08-30
    2
  • 草戊
    //全局变量 int i = 0; { //这里引用的是全局变量 i = 2; println(i); //输出:2 //允许在块里新创建一个同名的变量 int i = 3; println(i); //输出:3 } 您好,上面例子中 【i = 2;】这句话在块中使用全局变量,但是实际上此块中也定义了i变量,只不过位置比较靠后,这种情况,本文中的作用域有办法解决吗?如果优先在本块中查找,那么会不会找到自己块中的i,而不是全局的i呢?

    作者回复: 实际上,在每一行代码,可以见到的“可用表达式”的集合都是不一样的。也就是说,在每一行,你可以反问的变量都是不同的。 比如,在int i = 3之后,你访问的i,和在它前面访问的i,是不一样的。 也就是说,块作用域,并不是在整个块里可见,而是在这个块里变量声明之后才可见。 在28讲,数据流分析中,专门有活跃性分析的一个算法,会教会你确定在每个位置的可用变量。

    2019-12-22
    1
收起评论
显示
设置
留言
26
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部