This commit is contained in:
Akagi201 2016-04-04 19:50:06 +08:00
parent c13813ab6b
commit 35b3e2ef68
4 changed files with 11 additions and 17 deletions

View File

@ -84,7 +84,7 @@ func main() {
调用flag.Bool函数会创建一个新的对应布尔型标志参数的变量。它有三个属性第一个是的命令行标志参数的名字“n”然后是该标志参数的默认值这里是false最后是该标志参数对应的描述信息。如果用户在命令行输入了一个无效的标志参数或者输入`-h`或`-help`参数那么将打印所有标志参数的名字、默认值和描述信息。类似的调用flag.String函数将于创建一个对应字符串类型的标志参数变量同样包含命令行标志参数对应的参数名、默认值、和描述信息。程序中的`sep`和`n`变量分别是指向对应命令行标志参数变量的指针,因此必须用`*sep`和`*n`形式的指针语法间接引用它们。 调用flag.Bool函数会创建一个新的对应布尔型标志参数的变量。它有三个属性第一个是的命令行标志参数的名字“n”然后是该标志参数的默认值这里是false最后是该标志参数对应的描述信息。如果用户在命令行输入了一个无效的标志参数或者输入`-h`或`-help`参数那么将打印所有标志参数的名字、默认值和描述信息。类似的调用flag.String函数将于创建一个对应字符串类型的标志参数变量同样包含命令行标志参数对应的参数名、默认值、和描述信息。程序中的`sep`和`n`变量分别是指向对应命令行标志参数变量的指针,因此必须用`*sep`和`*n`形式的指针语法间接引用它们。
当程序运行时,必须在使用标志参数对应的变量之前调用flag.Parse函数用于更新每个标志参数对应变量的值之前是默认值。对于非标志参数的普通命令行参数可以通过调用flag.Args()函数来访问返回值对应对应一个字符串类型的slice。如果在flag.Parse函数解析命令行参数时遇到错误默认将打印相关的提示信息然后调用os.Exit(2)终止程序。 当程序运行时,必须在使用标志参数对应的变量之前调用flag.Parse函数用于更新每个标志参数对应变量的值之前是默认值。对于非标志参数的普通命令行参数可以通过调用flag.Args()函数来访问返回值对应对应一个字符串类型的slice。如果在flag.Parse函数解析命令行参数时遇到错误默认将打印相关的提示信息然后调用os.Exit(2)终止程序。
让我们运行一些echo测试用例 让我们运行一些echo测试用例

View File

@ -32,9 +32,9 @@ q := new(int)
fmt.Println(p == q) // "false" fmt.Println(p == q) // "false"
``` ```
当然也可能有特殊情况如果两个类型都是空的也就是说类型的大小是0例如`struct{}`和 `[0]int`, 有可能有相同的地址依赖具体的语言实现译注请谨慎使用大小为0的类型因为如果类型的大小位0好可能导致Go语言的自动垃圾回收器有不同的行为具体请查看`runtime.SetFinalizer`函数相关文档)。 当然也可能有特殊情况如果两个类型都是空的也就是说类型的大小是0例如`struct{}`和 `[0]int`, 有可能有相同的地址依赖具体的语言实现译注请谨慎使用大小为0的类型因为如果类型的大小为0的可能导致Go语言的自动垃圾回收器有不同的行为具体请查看`runtime.SetFinalizer`函数相关文档)。
new函数使用常见相对比较少,因为对应结构体来说,可以直接用字面量语法创建新变量的方法会更灵活§4.4.1)。 new函数使用通常相对比较少,因为对于结构体来说,直接用字面量语法创建新变量的方法会更灵活§4.4.1)。
由于new只是一个预定义的函数它并不是一个关键字因此我们可以将new名字重新定义为别的类型。例如下面的例子 由于new只是一个预定义的函数它并不是一个关键字因此我们可以将new名字重新定义为别的类型。例如下面的例子
@ -43,4 +43,3 @@ func delta(old, new int) int { return new - old }
``` ```
由于new被定义为int类型的变量名因此在delta函数内部是无法使用内置的new函数的。 由于new被定义为int类型的变量名因此在delta函数内部是无法使用内置的new函数的。

View File

@ -1,6 +1,6 @@
### 2.3.4. 变量的生命周期 ### 2.3.4. 变量的生命周期
变量的生命周期指的是在程序运行期间变量有效存在的时间间隔。对于在包一级声明的变量来说,它们的生命周期和整个程序的运行周期是一致的。而相比之下,局部变量的声明周期则是动态的:每次创建一个新变量的声明语句开始,直到该变量不再被引用为止,然后变量的存储空间可能被回收。函数的参数变量和返回值变量都是局部变量。它们在函数每次被调用的时候创建。 变量的生命周期指的是在程序运行期间变量有效存在的时间间隔。对于在包一级声明的变量来说,它们的生命周期和整个程序的运行周期是一致的。而相比之下,局部变量的声明周期则是动态的:每次创建一个新变量的声明语句开始,直到该变量不再被引用为止,然后变量的存储空间可能被回收。函数的参数变量和返回值变量都是局部变量。它们在函数每次被调用的时候创建。
例如下面是从1.4节的Lissajous程序摘录的代码片段 例如下面是从1.4节的Lissajous程序摘录的代码片段
@ -28,7 +28,7 @@ for t := 0.0; t < cycles*2*math.Pi; t += res {
在每次循环的开始会创建临时变量t然后在每次循环迭代中创建临时变量x和y。 在每次循环的开始会创建临时变量t然后在每次循环迭代中创建临时变量x和y。
那么Go语言的自动圾收集器是如何知道一个变量是何时可以被回收的呢这里我们可以避开完整的技术细节基本的实现思路是从每个包级的变量和每个当前运行函数的每一个局部变量开始通过指针或引用的访问路径遍历是否可以找到该变量。如果不存在这样的访问路径那么说明该变量是不可达的也就是说它是否存在并不会影响程序后续的计算结果。 那么Go语言的自动圾收集器是如何知道一个变量是何时可以被回收的呢?这里我们可以避开完整的技术细节,基本的实现思路是,从每个包级的变量和每个当前运行函数的每一个局部变量开始,通过指针或引用的访问路径遍历,是否可以找到该变量。如果不存在这样的访问路径,那么说明该变量是不可达的,也就是说它是否存在并不会影响程序后续的计算结果。
因为一个变量的有效周期只取决于是否可达,因此一个循环迭代内部的局部变量的生命周期可能超出其局部作用域。同时,局部变量可能在函数返回之后依然存在。 因为一个变量的有效周期只取决于是否可达,因此一个循环迭代内部的局部变量的生命周期可能超出其局部作用域。同时,局部变量可能在函数返回之后依然存在。
@ -52,6 +52,3 @@ func g() {
f函数里的x变量必须在堆上分配因为它在函数退出后依然可以通过包一级的global变量找到虽然它是在函数内部定义的用Go语言的术语说这个x局部变量从函数f中逃逸了。相反当g函数返回时变量`*y`将是不可达的,也就是说可以马上被回收的。因此,`*y`并没有从函数g中逃逸编译器可以选择在栈上分配`*y`的存储空间译注也可以选择在堆上分配然后由Go语言的GC回收这个变量的内存空间虽然这里用的是new方式。其实在任何时候你并不需为了编写正确的代码而要考虑变量的逃逸行为要记住的是逃逸的变量需要额外分配内存同时对性能的优化可能会产生细微的影响。 f函数里的x变量必须在堆上分配因为它在函数退出后依然可以通过包一级的global变量找到虽然它是在函数内部定义的用Go语言的术语说这个x局部变量从函数f中逃逸了。相反当g函数返回时变量`*y`将是不可达的,也就是说可以马上被回收的。因此,`*y`并没有从函数g中逃逸编译器可以选择在栈上分配`*y`的存储空间译注也可以选择在堆上分配然后由Go语言的GC回收这个变量的内存空间虽然这里用的是new方式。其实在任何时候你并不需为了编写正确的代码而要考虑变量的逃逸行为要记住的是逃逸的变量需要额外分配内存同时对性能的优化可能会产生细微的影响。
Go语言的自动垃圾收集器对编写正确的代码是一个巨大的帮助但也并不是说你完全不用考虑内存了。你虽然不需要显式地分配和释放内存但是要编写高效的程序你依然需要了解变量的生命周期。例如如果将指向短生命周期对象的指针保存到具有长生命周期的对象中特别是保存到全局变量时会阻止对短生命周期对象的垃圾回收从而可能影响程序的性能 Go语言的自动垃圾收集器对编写正确的代码是一个巨大的帮助但也并不是说你完全不用考虑内存了。你虽然不需要显式地分配和释放内存但是要编写高效的程序你依然需要了解变量的生命周期。例如如果将指向短生命周期对象的指针保存到具有长生命周期的对象中特别是保存到全局变量时会阻止对短生命周期对象的垃圾回收从而可能影响程序的性能

View File

@ -1,6 +1,6 @@
### 2.4.2. 可赋值性 ### 2.4.2. 可赋值性
赋值语句是显式的赋值形式,但是程序中还有很多地方会发生隐式的赋值行为:函数调用会隐式地将调用参数的值赋值给函数的参数变量,一个返回语句隐式地将返回操作的值赋值给结果变量一个复合类型的字面量§4.2)也会产生赋值行为。例如下面的语句: 赋值语句是显式的赋值形式,但是程序中还有很多地方会发生隐式的赋值行为:函数调用会隐式地将调用参数的值赋值给函数的参数变量,一个返回语句隐式地将返回操作的值赋值给结果变量一个复合类型的字面量§4.2)也会产生赋值行为。例如下面的语句:
```Go ```Go
medals := []string{"gold", "silver", "bronze"} medals := []string{"gold", "silver", "bronze"}
@ -20,6 +20,4 @@ map和chan的元素虽然不是普通的变量但是也有类似的隐式
可赋值性的规则对于不同类型有着不同要求对每个新类型特殊的地方我们会专门解释。对于目前我们已经讨论过的类型它的规则是简单的类型必须完全匹配nil可以赋值给任何指针或引用类型的变量。常量§3.6)则有更灵活的赋值规则,因为这样可以避免不必要的显式的类型转换。 可赋值性的规则对于不同类型有着不同要求对每个新类型特殊的地方我们会专门解释。对于目前我们已经讨论过的类型它的规则是简单的类型必须完全匹配nil可以赋值给任何指针或引用类型的变量。常量§3.6)则有更灵活的赋值规则,因为这样可以避免不必要的显式的类型转换。
对于两个值是否可以用`==`或`!=`进行相等比较的能力也和可赋值能力有关系:对于任何类型的值的相等比较,第二个值必须是对第一个值类型对应的变量是可赋值的,反之依然。和前面一样,我们会对每个新类型比较特殊的地方做专门的解释。 对于两个值是否可以用`==`或`!=`进行相等比较的能力也和可赋值能力有关系:对于任何类型的值的相等比较,第二个值必须是对第一个值类型对应的变量是可赋值的,反之亦然。和前面一样,我们会对每个新类型比较特殊的地方做专门的解释。