Skip to content

Latest commit

 

History

History
87 lines (55 loc) · 3.24 KB

002程序结构.md

File metadata and controls

87 lines (55 loc) · 3.24 KB

程序结构

指针

  • 每一个变量都有对应的内存地址
  • 值不一定有内存地址

指针在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

new

创建一个匿名变量,并返回指针, 这和新创建一个临时变量,返回临时变量的指针有不同吗? 没有任何区别,go中的new只是语法糖而已,不像c/c++中new是实实在在的功能。

为什么函数中能返回局部变量,而不出错,不管是传值还是传址?
在c++中,返回时传值还无所谓,传址会导致"不可预知的行为", 因为指针指向的数据有可能被回收了或重新使用了。 在go中,申请的局部变量(不管是不是new出来的),都是由go去判断, 是在堆上申请,还是栈上申请,这里就是go的逃逸策略。 这个逃逸策略:如果申请的变量出了函数就不用了,就在栈上创建, 反之就在堆上创建。这个逃逸策略是在编译期做决策。 这个逃逸策略远比上面描述的复杂,涉及到代码优化等。 就是因为在堆还是在栈完全是由go来决定,编写代码反而不用在意这些了。

因为go中的new是语法糖,一般用的很少。

变量的生命周期

  • 全局变量(包一级的变量)的生命周期和程序运行周期一致

  • 局部变量的生命周期是动态的

  • 在变量后加, 不会导致编译错误,这一特性常用于多行显示参数

go的逃逸策略会帮我们把变量放在堆或栈上,编码时是不需要考虑这块, 但是从性能上考虑,不应该把一个短生命周期的变量保存到长生命周期的对象中, 特别是全局变量,这会影响到性能。

赋值

  • 元组赋值的时候,左边变量的数目和右边的数目要一致
  • 例外也有(map查找、类型断言、通道接收),她们可以返回一个值也可以返回两个

类型

type 类型名 底层类型

就算底层类型是一样的,两个不同的类型是不能直接比较和隐式转换的

包和文件

  • 要导出的,可以单独放在一个文件里,具体的实现可以放在另一个文件

  • 一个包通常只有一个源文件有包注释,如果多个源文件都有包注释,工具会按源文件名顺序进行合并

  • 如果包注释很大,一般将包注释单独放到一个独立的doc.go文件中

  • 惯例:包名和导入路径的最后一个字段相同

  • 包的初始化会先找包级别变量的依赖顺序,然后按包级别出现顺序依次初始化

  • 有一些初始化比较复杂,可以放在init(),一个文件可以有多个init()初始化函数

  • 如果不使用init(),也可以使用匿名函数来初始化

  • 作用域和生命周期是不同概念

  • 作用域是编译期概念

  • 生命周期是运行期概念

:=这种方式遇到作用域时,很容易引发隐晦的bug,特别是在元组中,此时应该使用var 声明err