- 每一个变量都有对应的内存地址
- 值不一定有内存地址
指针在flag包中大量用到,因为指针不关心变量名,而是和变量的地址关联。
flag包中,如果不调用Parse,不会更新指针对应地址上的值
n := flag.Bool("n", false, "123")
s := flag.String("s", "456", "456")
fmt.Println(*n, *s, n, s)
flag.Parse()
fmt.Println(*n, *s, n, s)
// go run initFlag.go -n true
// false 456 0xc00007c00f 0xc0000701d0
// true 456 0xc00007c00f 0xc0000701d0
创建一个匿名变量,并返回指针, 这和新创建一个临时变量,返回临时变量的指针有不同吗? 没有任何区别,go中的new只是语法糖而已,不像c/c++中new是实实在在的功能。
为什么函数中能返回局部变量,而不出错,不管是传值还是传址?
在c++中,返回时传值还无所谓,传址会导致"不可预知的行为",
因为指针指向的数据有可能被回收了或重新使用了。
在go中,申请的局部变量(不管是不是new出来的),都是由go去判断,
是在堆上申请,还是栈上申请,这里就是go的逃逸策略。
这个逃逸策略:如果申请的变量出了函数就不用了,就在栈上创建,
反之就在堆上创建。这个逃逸策略是在编译期做决策。
这个逃逸策略远比上面描述的复杂,涉及到代码优化等。
就是因为在堆还是在栈完全是由go来决定,编写代码反而不用在意这些了。
因为go中的new是语法糖,一般用的很少。
-
全局变量(包一级的变量)的生命周期和程序运行周期一致
-
局部变量的生命周期是动态的
-
在变量后加, 不会导致编译错误,这一特性常用于多行显示参数
go的逃逸策略会帮我们把变量放在堆或栈上,编码时是不需要考虑这块, 但是从性能上考虑,不应该把一个短生命周期的变量保存到长生命周期的对象中, 特别是全局变量,这会影响到性能。
- 元组赋值的时候,左边变量的数目和右边的数目要一致
- 例外也有(map查找、类型断言、通道接收),她们可以返回一个值也可以返回两个
type 类型名 底层类型
就算底层类型是一样的,两个不同的类型是不能直接比较和隐式转换的
-
要导出的,可以单独放在一个文件里,具体的实现可以放在另一个文件
-
一个包通常只有一个源文件有包注释,如果多个源文件都有包注释,工具会按源文件名顺序进行合并
-
如果包注释很大,一般将包注释单独放到一个独立的doc.go文件中
-
惯例:包名和导入路径的最后一个字段相同
-
包的初始化会先找包级别变量的依赖顺序,然后按包级别出现顺序依次初始化
-
有一些初始化比较复杂,可以放在init(),一个文件可以有多个init()初始化函数
-
如果不使用init(),也可以使用匿名函数来初始化
-
作用域和生命周期是不同概念
-
作用域是编译期概念
-
生命周期是运行期概念
:=这种方式遇到作用域时,很容易引发隐晦的bug,特别是在元组中,此时应该使用var 声明err