欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

《Go语言圣经》阅读笔记:第二章程序结构

程序员文章站 2024-02-03 10:05:40
...

第二章 程序结构

2.1 命名

在GO语言中,所有的变量名、函数、常量、类型、语句标号、包名都遵循一个原则:

名字必须以字母或者下划线开头,后面紧跟任意数量的字母数字下划线。区分大小写。

在GO语言中包含25个关键字:

| break   | default   | func   | interface | select |
| case    | defer     |  go    |  map      | struct |
| chan    | else      | goto   | package   | switch |
| const   |fallthrough|  if    | range     | type   |
| continue| for       | import | return    | var    |

注意:GO语言中没有while关键字…

在Go语言中大约有30多个预定义的名字,主要为内建的常量、变量以及函数。

内建常量:true false iota nil

内建类型:int int8 int16 int32 int64
         uint uint8 uint16 uint32 uint64
         float32 float64 complex128 complex64
         bool byte rune string error

内建函数:make len cap new append copy close delete
         complex real imag 
         panic recover

一般情况情况下:名字的首字母大小写决定了名字在包外的可见性。如果包外可见,则使用以大写字母开头,如Println()
Go语言命名遵循"驼峰式"原则,即变量有多个字母混合组成时,首字母大写的方式,不是采用下划线分割的方式。

2.2 声明

声明语句定义了程序各种实体对象,以及他们的部分或者全部属性。Go语言中主要包含4种类型的声明语句:var、const、type、func,他们分别对应变量、常量、类型、函数实体对象的声明。

一个Go语言编写的程序对应一个或多个以.go为文件后缀名的源文件中。

  • 每个源文件以包的声明语句开始,说明该源文件是属于哪个包。
  • 包声明语句之后是import语句导入依赖的其它包,
  • 然后是包一级的类型、变量、常量、函数的声明语句,包一级的各种类型的声明语句的顺序无关紧要。

例如,下面的例子中声明了一个常量、一个函数和两个变量:

package main

import "fmt"

/*水的沸点*/
const boilingF = 212.0

func main() {
	var f = boilingF
	var c = (f - 32) * 5 / 9

	fmt.Printf("boiling point = %g°F or %g°C", f, c)
}

结果如下:

boiling point = 212°F or 100°C

函数和变量的使用如下:

package main

import "fmt"

func main() {
	const freezingF, boilingF = 32.0, 212.0

	fmt.Printf("%g°F = %g°C\n", freezingF, ftoc(freezingF))
	fmt.Printf("%g°F = %g°C\n", boilingF, ftoc(boilingF))

}

func ftoc(f float64) float64 {
	return (f - 32) * 5 / 9
}

运行结果:

32°F = 0°C
212°F = 100°C

2.3 变量

var声明语句可以创建一个特定类型的变量,然后给变量附加一个名字,而且设置变量的初始值。变量的声明语法如下:

var 变量名 类型 = 表达式

其中"类型"或者"=表达式"可以省略其中一个。

  • 如果省略"类型",则根据初始值表达式推导出变量的类型
  • 如果省略"=表达式", 则将其初始化为该类型的0值

在Go语言中,零值初始化机制可以保证每一个声明的变量都有一个很好的初始值,不存在未初始化的变量。

包级别声明的变量会在main入口函数执行前完成初始化,局部变量将会在声明语句被执行到的时候在完成初始化。
一组变量也可以通过条用一个函数,有函数的多个返回值来完成初始化。

    var f, err = os.Open("/usr/local/avi/xxoo.avi")/*os.Open return a file and an error*/

2.3.1 简短声明

在函数内部,有一个简短变量声明方式可用于声明和初始化局部变量。它以"名字:=表达式"形式声明变量。变量的类型根据表达式来自动推导。

简短声明因为简洁和灵活的特点,被广泛用于大部*部变量的声明和初始化。

var形式的声明语句往往用于需要显式指定变量类型的地方,或者因为变量稍后重新复制而初始值无关紧要的地方。

i := 100
var boiling float64 = 100.0
var names []string
var err error
var p Point

与var形式声明变量语句一样,简短声明也可以用来声明和初始化一组变量:
i, j := 1, 2

简短声明有一个需要注意的地方:

变量 := 表达式

  • 简短声明左边的变量可能不是全部刚刚声明的,允许使用先前已经声明的变量,如果已经声明了,那么这里只是赋值操作。
  • 简短声明中必须至少要声明一个新的变量,否则无法编译通过。
  • 简短声明语句只有对已经存在的同级词法域声明过的变量才和赋值操作等价。
    fp, err := os.Open(xxx.avi)
    
    /* xxoo blabla*/

    newFp, err := os.Create(ooo.avi)

上面使用简短声明的方式没有语法错误,但是下面的简短声明则无法编译通过:

    fp, err := os.Open(xxx.avi)
    
    /* xxoo blabla*/

    fp, err := os.Create(ooo.avi)

2.3.2 指针

基本原则同C语言。

    x := 1
    p := &x
  • 任何类型的指针的零值都是nil
  • 在Go语言中,返回函数中的局部变量的地址也是安全的…

2.3.3 new函数

另一个创建变量的方法是调用用内建的new函数。
表达式new(T)将创建一个T类型的匿名变量,初始化为T类型的零值,然后返回变量地址,返回的指针类型为*T。

    p := new(int)
    fmt.Println(*p)
    *p = 2
    fmt.Println(*p)

运行结果:

    0
    2

用new创建变量和普通变量声明语句方式创建变量没有什么区别,除了不需要声明一个临时变量的名字外,我们还可以在表达式中使用new(T)。
使用new函数来声明变量比较少见。

在Go允许存在大小为0的数据,结构体…
new 是Go语言内置的变量名。

2.3.4 变量的生命周期

Go语言存在垃圾回收机制。
Go语言的自动垃圾收集器对编写正确的代码是一个巨大的帮助,但也并不是说你完全不用考虑内存了。
你虽然不需要显式地分配和释放内存,但是要编写高效的程序你依然需要了解变量的生命周期。
例如,如果将指向短生命周期对象的指针保存到具有长生命周期的对象中,特别是保存到全局变量时,会阻止对短生命周期对象的垃圾回收(局部变量逃逸)。

  • 局部变量逃逸
    var global *int
    
    func f(){
        var x int
        x = 1
        global = &x
    }

在Go语言中,上述代码是合法的,但是由于x是函数内的局部变量,却将其保存到全局变量中,导致声明周期延长。Go的术语称之为:“局部变量x从函数f中逃逸了”

2.4 赋值

赋值操作比较简单,直接举几个例子来说明:

    x = 1
    *p = true
    person.name = "Toney"
    count[x] = count[x] * scale
    count[x] *= scale

赋值变量支持递增(++)和递减(--)语句。

自增和自减是语句,不是表达式,因此x = i++是不合法的。

    v := 1
    v++     //合法
    v--     //合法

2.4.1 元组赋值

元组赋值是同时对多个变量进行赋值的操作。
在赋值之前,赋值语句右边的所有的表达式会首先计算求值,然后在同一更新右边变量的值。

例如交换两个数据:

/*直接交换两个变量的值*/
    x,y = y,x  
    a[i],a[j] = a[j],a[i]

/*多个变量进行赋值*/
    i,j,k = 1,2,3
    f, err = os.Open("golang.txt")

    v, ok = m[key]  //查找哈希MAP
    v, ok = x.(T)   //类型断言
    v, ok = <-ch    //接收通道数据

如果对于某个返回值不感兴趣,则可以使用’_'来代替,表示不关心此返回值:

    _, ok = m[key]           //返回2个值
    _, ok = mm.[""], false   //返回1个值
    _ = mm[""]               //返回1个值

2.4.2 可赋值性

赋值语句是显式的赋值形式

    medals := []string{"gold", "silver","water","soil"}

也可以单独每一个元素进行赋值:

    medals[0] = "gold"
    medals[1] = "silver"
    medals[2] = "water"
    medals[3] = "soil"

无论是显式赋值还是隐式赋值,等号两边的类型必须相同。

2.5 类型

变量或表达式的类型定义了对应存储值的属性特征。

格式如下:

type 类型名字 底层类型

  • 类型声明语句一般出现在包一级,如果新创建的类型名字首字符大写,则在外部包也可以使用。
  • 两个拥有不同类型的值无法直接进行比较

2.6 包和文件

  • Go语言中的包和其他语言的库或模块的概念类似,目的都是为了支持模块化、封装、单独编译和代码重用
  • 每个包都对应一个独立的名字空间。
  • 包还可以让我们通过控制哪些名字是外部可见的来隐藏内部实现信息。
  • 在Go语言中,一个简单的规则是:如果一个名字是大写字母开头的,那么该名字是导出的
  • 注:由于汉字不区分大小写,因此无法导出。

2.7 作用域

  • 一个声明语句将程序中的实体和一个名字关联,比如一个函数或一个变量。
  • 声明语句的作用域是指源代码中可以有效使用这个名字的范围。

作用域和生命周期是两个不同的概念:

  • 作用域对应源代码的文本区域,是一个编译时的属性
  • 变量的生存周期是指运行时变量存在的有效时间段,在此时间区域内,可以被其他部分引用。

注意事项:

  • Go中内部声明会屏蔽同名的外部声明,因此需要特别注意简短声明方式
相关标签: GO入门