第2章,部分字词修订。

This commit is contained in:
zhliner
2017-08-24 22:25:47 +08:00
parent 622f7fcc2c
commit 9f90d30fa7
9 changed files with 22 additions and 22 deletions

View File

@@ -4,9 +4,9 @@
不要将作用域和生命周期混为一谈。声明语句的作用域对应的是一个源代码的文本区域;它是一个编译时的属性。一个变量的生命周期是指程序运行时变量存在的有效时间段,在此时间区域内它可以被程序的其他部分引用;是一个运行时的概念。
句法块是由花括弧所包含的一系列语句就像函数体或循环体花括弧包裹的内容一样。句法块内部声明的名字是无法被外部块访问的。这个块决定了内部声明的名字的作用域范围。我们可以把块block的概念推广到包括其他声明的群组这些声明在代码中并未显式地使用花括号包裹起来我们称之为词法块。对全局的源代码来说存在一个整体的词法块称为全局词法块对于每个包每个for、if和switch语句也都对应词法块每个switch或select的分支也有独立的法块;当然也包括显式书写的词法块(花括弧包含的语句)。
句法块是由花括弧所包含的一系列语句就像函数体或循环体花括弧包裹的内容一样。句法块内部声明的名字是无法被外部块访问的。这个块决定了内部声明的名字的作用域范围。我们可以把块block的概念推广到包括其他声明的群组这些声明在代码中并未显式地使用花括号包裹起来我们称之为词法块。对全局的源代码来说存在一个整体的词法块称为全局词法块对于每个包每个for、if和switch语句也都对应词法块每个switch或select的分支也有独立的法块;当然也包括显式书写的词法块(花括弧包含的语句)。
声明语句对应的词法域决定了作用域范围的大小。对于内置的类型、函数和常量比如int、len和true等是在全局作用域的因此可以在整个程序中直接使用。任何在函数外部也就是包级语法域声明的名字可以在同一个包的任何源文件中访问的。对于导入的包例如tempconv导入的fmt包则是对应源文件级的作用域因此只能在当前的文件中访问导入的fmt包当前包的其它源文件无法访问在当前源文件导入的包。还有许多声明语句比如tempconv.CToF函数中的变量c则是局部作用域的它只能在函数内部甚至只能是局部的某些部分访问。
声明语句对应的词法域决定了作用域范围的大小。对于内置的类型、函数和常量比如int、len和true等是在全局作用域的因此可以在整个程序中直接使用。任何在函数外部也就是包级语法域声明的名字可以在同一个包的任何源文件中访问的。对于导入的包例如tempconv导入的fmt包则是对应源文件级的作用域因此只能在当前的文件中访问导入的fmt包当前包的其它源文件无法访问在当前源文件导入的包。还有许多声明语句比如tempconv.CToF函数中的变量c则是局部作用域的它只能在函数内部甚至只能是局部的某些部分访问。
控制流标号就是break、continue或goto语句后面跟着的那种标号则是函数级的作用域。
@@ -44,7 +44,7 @@ func main() {
`x[i]``x + 'A' - 'a'`声明语句的初始化的表达式中都引用了外部作用域声明的x变量稍后我们会解释这个。注意后面的表达式与unicode.ToUpper并不等价。
正如上面例子所示并不是所有的词法域都显式地对应到由花括弧包含的语句还有一些隐含的规则。上面的for语句创建了两个词法域花括弧包含的是显式的部分是for的循环体部分词法域另外一个隐式的部分则是循环的初始化部分比如用于迭代变量i的初始化。隐式的词法域部分的作用域还包含条件测试部分和循环后的迭代部分`i++`),当然也包含循环体词法域。
正如上面例子所示并不是所有的词法域都显式地对应到由花括弧包含的语句还有一些隐含的规则。上面的for语句创建了两个词法域花括弧包含的是显式的部分是for的循环体部分词法域另外一个隐式的部分则是循环的初始化部分比如用于迭代变量i的初始化。隐式的词法域部分的作用域还包含条件测试部分和循环后的迭代部分`i++`),当然也包含循环体词法域。
下面的例子同样有三个不同的x变量每个声明在不同的词法域一个在函数体词法域一个在for隐式的初始化词法域一个在for循环体词法域只有两个块是显式创建的
@@ -71,7 +71,7 @@ if x := f(); x == 0 {
fmt.Println(x, y) // compile error: x and y are not visible here
```
第二个if语句嵌套在第一个内部因此第一个if语句条件初始化词法域声明的变量在第二个if中也可以访问。switch语句的每个分支也有类似的词法域规则条件部分为一个隐式词法域然后每个是每个分支的词法域。
第二个if语句嵌套在第一个内部因此第一个if语句条件初始化词法域声明的变量在第二个if中也可以访问。switch语句的每个分支也有类似的词法域规则条件部分为一个隐式词法域然后是每个分支的词法域。
在包级别,声明的顺序并不会影响作用域范围,因此一个先声明的可以引用它自身或者是引用后面的一个声明,这可以让我们定义一些相互嵌套或递归的类型或函数。但是如果一个变量或常量递归引用了自身,则会产生编译错误。
@@ -85,7 +85,7 @@ f.ReadByte() // compile error: undefined f
f.Close() // compile error: undefined f
```
变量f的作用域只在if语句内因此后面的语句将无法引入它这将导致编译错误。你可能会收到一个局部变量f没有声明的错误提示具体错误信息依赖编译器的实现。
变量f的作用域只在if语句内因此后面的语句将无法引入它这将导致编译错误。你可能会收到一个局部变量f没有声明的错误提示具体错误信息依赖编译器的实现。
通常需要在if之前声明变量这样可以确保后面的语句依然可以访问变量
@@ -112,7 +112,7 @@ if f, err := os.Open(fname); err != nil {
但这不是Go语言推荐的做法Go语言的习惯是在if中处理错误然后直接返回这样可以确保正常执行的语句不需要代码缩进。
要特别注意短变量声明语句的作用域范围,考虑下面的程序,它的目的是获取当前的工作目录然后保存到一个包级的变量中。这可以本来通过直接调用os.Getwd完成但是将这个从主逻辑中分离出来可能会更好特别是在需要处理错误的时候。函数log.Fatalf用于打印日志信息然后调用os.Exit(1)终止程序。
要特别注意短变量声明语句的作用域范围,考虑下面的程序,它的目的是获取当前的工作目录然后保存到一个包级的变量中。这本来可以通过直接调用os.Getwd完成但是将这个从主逻辑中分离出来可能会更好特别是在需要处理错误的时候。函数log.Fatalf用于打印日志信息然后调用os.Exit(1)终止程序。
```Go
var cwd string
@@ -127,7 +127,7 @@ func init() {
虽然cwd在外部已经声明过但是`:=`语句还是将cwd和err重新声明为新的局部变量。因为内部声明的cwd将屏蔽外部的声明因此上面的代码并不会正确更新包级声明的cwd变量。
由于当前的编译器会检测到局部声明的cwd并没有使用然后报告这可能是一个错误但是这种检测并不可靠。因为一些小的代码变更例如增加一个局部cwd的打印语句就可能导致这种检测失效。
由于当前的编译器会检测到局部声明的cwd并没有使用然后报告这可能是一个错误但是这种检测并不可靠。因为一些小的代码变更例如增加一个局部cwd的打印语句就可能导致这种检测失效。
```Go
var cwd string