Go圣经-学习笔记之程序结构


声明:本文转载自https://my.oschina.net/u/3287304/blog/1554468,转载目的在于传递更多信息,仅供学习交流之用。如有侵权行为,请联系我,我会及时删除。

上一篇 Go圣经-学习笔记入门bufio.Writer

变量声明

它包括四种类型:const常量,type类型,func函数和var变量。

var 变量名字 类型= 表达式 

如果去掉= 表达式, 则Go语言将使用零值初始化该变量。数值类型变量对应的值的0,bool类型变量对应的零值是false,字符串类型变量对应的零值是空字符串,接口或者引用类型(包括slice、map、chan和函数)变量对应的零值是nil。数组或者结构体变量对应的零值是各个元素对应的零值。所以,go语言中不存在没有初始化的类型变量

这里要讨论一个问题:我们知道slice类型实际底层是struct结构体类型: struct { byte *array; uintgo len; uintgo cap; }; 理应slice零值是各个元素的初始化零值,实际上slice在Go语言内部把slice当做了一种新的类型。slices类型:types.TSLICE和struct类型:types.TSTRUCT, 所以初始化零值的策略也有所不同。

简单的逃逸分析

先来个DEMO

var p= f()  func f() *int{     v:=1     return &v } 

这段程序跑起来不会发生段错误,非法地址。知道其他主流语言的开发人员,可能会认为v变量是在stack上分配的,函数执行完成,则函数的stack被释放到,这是返回值非法引用v变量,则导致程序崩溃。

但是Go语言编译器会静态分析源码,它发现f函数外部有引用局部变量v。则v在函数内的内存空间分配是在heap上分配,使得p可以使用它,这就是一个简单的逃逸分析。大家可以网上查查编译器的逃逸分析。至于内存的释放是由Go语言提供的自动垃圾回收机制做的,不需要人工参与。

指针的价值

指针特别有价值的地方在于,我们可以不用知道变量的名字而访问一个变量。言外之意,只要你给我一块内存地址,给我这个这块内存地址块的数据类型,我就可以正确访问原来这个变量值。1.通过内存地址,则可以知道数据的起始位置;2.通过数据类型,则可以知道,简单数据类型或者复杂数据类型中的各个元素所占内存空间的大小,所以,我们就可以通过指针正确访问内存变量值。但是一个缺点在于:在垃圾回收时,要找到一个变量的所有访问者并不容易,我们必须知道所有变量全部的别名。只有在所有变量不在使用这块内存时,我们才能回收内存。

标准库flag简单使用

借助于上面的指针理解,我们现在分析下flag标准库的部分使用。

可能有些开发者经常会看到形如下面的使用,但不怎么会使用:

var n = flag.Bool("n", false, "omit trailing newline") var sep = flag.String("sep", " ", "separator")  func main(){     flag.Parse()     fmt.Print(strings.Join(flag.Args(), *sep))     if !*n{         fmt.Println()     } } 

有童鞋可能想问,为啥n和sep没有任何赋值操作, main函数就可以直接使用这两个指针变量了呢?因为这个程序在运行时,有一个全局变量CommandLine, 当flag.Parse解析os.Stdin的所有输入参数时,把CommandLine的指针变量n和sep全部赋值,同时通过flag.Args方法,可以获取到用户输入的数据列表。明白了这个,就知道怎么使用flag包了。

注意一点:对于bool变量,只需要:./program -n -sep , hello world , 输出: hello,world,且不换行。如果去掉-n, 则自动换行。

变量的生命周期

变量的生命周期是指在程序运行期间变量有效存在的时间间隔。

  1. 对于包级别的变量来说,它们的生命周期和整个程序的运行周期是一致的;
  2. 对于局部变量的生命周期则是动态的: 从每次创建一个变量开始,直到这个变量不再被任何别名引用为止,然后内存空间才会被回收
  3. 函数的输入参数和输出参数都是局部变量,它们在函数每次被调用的时候创建,调用完成则释放。

垃圾收集器的基本思路:从每个包级别和每个当前运行函数的局部变量开始,通过指针或者引用的方式遍历路径,是否可以找到该变量。如果找不到,则变量不可达。

一个变量的生命周期只取决于是否可达,因此局部变量的生命周期可能会超出其作用域,所以局部变量可能在函数返回之后依然存在。编译器会自动选择变量是分配在heap上还是stack上。这个选择不是由var或者new决定的

var global *int  func f() { // 因为global引用了x,所以x变量逃逸了,分配在heap上     var x int     x = 1     global = &x }  func g() { // 因为外部没有引用y,y不可达,分配在stack上。     y := new(int)     *y = 1 } 

元组赋值

x, y = y, x+y 

在赋值之前,赋值语句右边的所有表达式将会先进行求值,然后再统一更新左边对应变量的值

本文发表于2017年10月22日 14:34
(c)注:本文转载自https://my.oschina.net/u/3287304/blog/1554468,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如有侵权行为,请联系我们,我们会及时删除.

阅读 2188 讨论 0 喜欢 0

抢先体验

扫码体验
趣味小程序
文字表情生成器

闪念胶囊

你要过得好哇,这样我才能恨你啊,你要是过得不好,我都不知道该恨你还是拥抱你啊。

直抵黄龙府,与诸君痛饮尔。

那时陪伴我的人啊,你们如今在何方。

不出意外的话,我们再也不会见了,祝你前程似锦。

这世界真好,吃野东西也要留出这条命来看看

快捷链接
网站地图
提交友链
Copyright © 2016 - 2021 Cion.
All Rights Reserved.
京ICP备2021004668号-1