Go程序设计语言2.7 作用域-阿里云开发者社区

开发者社区> 华章计算机> 正文

Go程序设计语言2.7 作用域

简介:
+关注继续查看

2.7 作用域


声明将名字和程序实体关联起来,如一个函数或一个变量。声明的作用域是指用到声明时所声明名字的源代码段。

不要将作用域和生命周期混淆。声明的作用域是声明在程序文本中出现的区域,它是一个编译时属性。变量的生命周期是变量在程序执行期间能被程序的其他部分所引用的起止时间,它是一个运行时属性。

语法块(block)是由大括号围起来的一个语句序列,比如一个循环体或函数体。在语法块内部声明的变量对块外部不可见。块把声明包围起来,并且决定了它的可见性。我们可以把块的概念推广到其他没有显式包含在大括号中的声明代码,将其统称为词法块。包含了全部源代码的词法块,叫作全局块。每一个包,每一个文件,每一个for、if和switch语句,以及switch和select语句中的每一个条件,都是写在一个词法块里的。当然,显式写在大括号语法里的代码块也算是一个词法块。

一个声明的词法块决定声明的作用域大小。像int、len和true等内置类型、函数或常量在全局块中声明并且对于整个程序可见。在包级别(就是在任何函数外)的声明,可以被同一个包里的任何文件引用。导入的包(比如在tempconv例子中的fmt)是文件级别的,所以它们可以在同一个文件内引用,但是不能在没有另一个import语句的前提下被同一个包中其他文件中的东西引用。许多声明(像tempconv.CToF函数中变量c的声明)是局部的,仅可在同一个函数中或者仅仅是函数的一部分所引用。

控制流标签(如break、continue和goto语句使用的标签)的作用域是整个外层的函数(enclosing function)。

一个程序可以包含多个同名的声明,前提是它们在不同词法块中。例如可以声明一个和包级别变量同名的局部变量。或者像2.3.3节展示的,可以声明一个叫作new的参数,即使它是一个全局块中预声明的函数。然而,不要滥用,重声明所涉及的作用域越广,越可能影响其他的代码。

当编译器遇到一个名字的引用时,将从最内层的封闭词法块到全局块寻找其声明。如果没有找到,它会报“undeclared name”错误;如果在内层和外层块都存在这个声明,内层的将先被找到。这种情况下,内层声明将覆盖外部声明,使它不可访问:

 

 

在函数里面,词法块可能嵌套很深,所以一个局部变量声明可能覆盖另一个。很多词法块使用if语句和for循环这类控制流结构构建。下面的程序有三个称为x的不同的变量声明,因为每个声明出现在不同的词法块。(这个例子只是用来说明作用域的规则,风格并不完美!)

 

表达式x[i]和x + 'A' - 'a'都引用了在外层声明的x,稍后我们会解释它。(注意,后面的表达式不同于unicode.ToUpper函数。)

如上所述,不是所有的词法块都对应于显式大括号包围的语句序列,有一些词法块是隐式的。for循环创建了两个词法块:一个是循环体本身的显式块,以及一个隐式块,它包含了一个闭合结构,其中就有初始化语句中声明的变量,如变量i。隐式块中声明的变量的作用域包括条件、后置语句(i++),以及for语句体本身。

下面的例子也有三个名字为x的变量,每一个都在不同的词法块中声明:一个在函数体中,一个在for语句块中,一个在循环体中。但只有两个块是显式的:

 

像for循环一样,除了本身的主体块之外,if和switch语句还会创建隐式的词法块。下面的if-else链展示x和y的作用域:

 

第二个if语句嵌套在第一个中,所以第一个语句的初始化部分声明的变量在第二个语句中是可见的。同样的规则可以应用于switch语句:条件对应一个块,每个case语句体对应一个块。

在包级别,声明的顺序和它们的作用域没有关系,所以一个声明可以引用它自己或者跟在它后面的其他声明,使我们可以声明递归或相互递归的类型和函数。如果常量或变量声明引用它自己,则编译器会报错。

在以下程序中:

 

f变量的作用域是if语句,所以f不能被接下来的语句访问,编译器会报错。根据编译器的不同,也可能收到其他报错:局部变量f没有使用。

所以通常需要在条件判断之前声明f,使其在if语句后面可以访问:

 

你可能希望避免在外部块中声明f和err,方法是将Stat和Close的调用放到else块中:

 

通常Go中的做法是在if块中处理错误然后返回,这样成功执行的路径不会被变得支离破碎。

短变量声明依赖一个明确的作用域。考虑下面的程序,它获取当前的工作目录然后把它保存在一个包级别的变量里。这通过在main函数中调用os.Getwd来完成,但是最好可以从主逻辑中分离,特别是在获取目录失败是致命错误的情况下。函数log.Fatalf输出一条消息,然后调用os.Exit(1)退出。

 

因为cwd和err在init函数块的内部都尚未声明,所以:=语句将它们都声明为局部变量。内层cwd的声明让外部的声明不可见,所以这个语句没有按预期更新包级别的cwd变量。

当前Go编译器检测到局部的cwd变量没有被使用,然后报错,但是不必严格执行这种检查。进一步做一个小的修改,比如增加引用局部cwd变量的日志语句就可以让检查失效。

 

 

全局的cwd变量依然未初始化,看起来一个普通的日志输出让bug变得不明显。

处理这种潜在的问题有许多方法。最直接的方法是在另一个var声明中声明err,避免使用:=。

 

现在我们已经看到包、文件、声明以及语句是如何来构成程序的。接下来的两章将要讨论数据的结构。

 

 

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
《Java 7程序设计入门经典》一2.5 变量的作用域和生命周期
本节书摘来自华章出版社《Java 7程序设计入门经典》一书中的第2章,第2.5节,作者(美)Herbert Schildt Dale Skrien,更多章节内容可以访问云栖社区“华章计算机”公众号查看
733 0
C语言中关键字auto、static、register、const、volatile、extern的作用
原文:C语言中关键字auto、static、register、const、volatile、extern的作用 关键字auto、static、register、const、volatile、extern这些关键词都是c++基础知识,我整理了一下,希望对新学的朋友们有用:(1)auto   这个这个关键字用于声明变量的生存期为自动,即将不在任何类、结构、枚举、联合和函数中定义的变量视为全局变量,而在函数中定义的变量视为局部变量。
1107 0
阿里云服务器端口号设置
阿里云服务器初级使用者可能面临的问题之一. 使用tomcat或者其他服务器软件设置端口号后,比如 一些不是默认的, mysql的 3306, mssql的1433,有时候打不开网页, 原因是没有在ecs安全组去设置这个端口号. 解决: 点击ecs下网络和安全下的安全组 在弹出的安全组中,如果没有就新建安全组,然后点击配置规则 最后如上图点击添加...或快速创建.   have fun!  将编程看作是一门艺术,而不单单是个技术。
4617 0
JS闭包以及作用域初探
    以前看到的一个问题,很有意思: for (var i = 0; i < 5; i++) { setTimeout(function () { console.
672 0
指针,c语言关键字,作用域,生命周期,内存数据存储
 作用域:变量可被访问的有效范围。 生命周期:变量存储空间的有效生存时间。 extern int c;   通过关键字extern来告诉程序int c;是别的程序中定义的。 extern int fun(void); 表示这个方式是在其它的文件中定义的。 全局变量在程序的任何地方都可以被调用到,生命周期是从程序启动到程序
1041 0
10059
文章
0
问答
来源圈子
更多
+ 订阅
文章排行榜
最热
最新
相关电子书
更多
文娱运维技术
立即下载
《SaaS模式云原生数据仓库应用场景实践》
立即下载
《看见新力量:二》电子书
立即下载