Go语言学习笔记

一、Go语言基本语法

1. 命名和声明

和其他语言一样,Go语言也有其关键字和内部类型以及一些内部函数。在这里,不记录这些,实际开发中慢慢去学习这些具体的东西。对于一门语言,我们最先了解的必须它的结构和基本逻辑。不过在此之前,得先了解下声明,Go语言的声明类型主要是四个:

  • var
  • const
  • type
  • func

顾名思义,通过名字我们大致可以看出这四个声明的意思。不过这里我们重点学习下变量和类型的声明。和其他语言一样,变量声明之后才可以使用,直接给个例子:

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\n", f, c)
}

其中,func声明的main是程序的入口,主函数。凡是main函数所在的文件,包都必须是package main。fmt是Go语言内部的输出函数所在的包,boilingF,f和c就是三个定义的变量。形式例如: 类型 变量名 = 赋值。

变量声明其实有几种方式,如果声明时并不赋值,可以这样:

var i, j, k int // int, int, int

如果声明时赋值,可以这样:

var b, f, s = true, 2.3. "four" // bool, float64, string

当然也可以调用一个函数进行赋值:

var f, err = os.Open(name)

还有一种简短声明变量的语句,不需要定义类型,但是必须给出初始值。形式是这样的:变量名 := 表达式,例子如下:

anim := gif.GIF{LoopCount: nframes}
freq := rand.Float64() * 3.0
t := 0.0
var i, j := 0, 1

2.指针

我的感觉是,指针的存在让Go语言多了一份神秘,也让其理解难度加大了一点,当然,Go的指针没有C/C++那样的复杂。定义是一样的,指针就是储存变量的地址。我们定义一个指针:

x := 1
p := &x
*p = 2 // 次数 x = 2

这里,x是变量名,&表示取地址,所以指针p自然就是&x。当对*p进行赋值时,本质就是对x进行赋值。

在Go语言中,有一个new函数,表达式为new(T),表示创建一个T类型的匿名变量,初始化的值为零值,返回变量的地址,指针类型为*T。

p := new(int) // p, *int 类型,指向匿名的int变量
fmt.Println(*p) // "0"
*p = 2 // 设置int匿名变量的值为2
fmt.Println(*p) // "2"

new函数只是一个语法糖,并不是一个新的基础概念。 对于变量,是有一定的生命周期的,也就是变量早程序运行期间的有效存在的时间间隔。对于包一级的变量,它们的生命周期是整个程序的运行时间。局部变量的生命周期则是动态的:每次从创建该变量开始,知道该变量不再被引用为止,然后变量的储存空间可能会被回收。

3.赋值

我们使用赋值语句赋值,最简单的形式就是 被赋值的变量 = 赋的新值。例如:

x = 1 // 命名变量的赋值
*p = true // 通过指针间接赋值
person.name = 'bob' // 结构体字段赋值
count[x] = count[x] * scale // 数组、slice或者map的元素赋值

Go语言中支持对于元组的赋值,例如:

x, y = y, x //交换x和y的值
a[i], a[j] = a[j], a[i] //交换数组中两个位置的值

4.类型

一个类型声明语句创建了一个新的类型名称,和现有类型具有相同的底层结构。新命名的类型提供了一个方法,用来分割不同概念的类型,这样即使它们底层类型相同也是不兼容的。 type 类型名字 底层类型

类型声明语句一般是出现在包一级,因此如果新创建的类型名字的首字符大写,则在外部包也可以使用。我们给出一个例子说明底层类型相同的不同类型:

package main

import "fmt"

type Celsius    float64 //摄氏温度
type Fahrenheit float64 //华氏温度

const (
  AbsoluteZeroC Celsius = -273.15
  FreezingC     Celsius = 0
  BoilingC      Celsius = 100
)
func CToF(c Celsius) Fahrenheit { return Fahrenheit(c*9/5 + 32) }
func FToC(f Fahrenheit) Celsius { return Celsius((f - 32) * 5 / 9)}

这里我们生命了两种类型:Celsius和Fahrenheit,分别对应不同的温度单位。尽管他们拥有一样的底层类型float64,但是他们又是不同的数据类型,所以他它们不能被相互比较或者混合在一个表达式里进行运算。刻意区分类型,刻意避免一些像无意中使用不同单位的温度混合计算导致的错误。这里需要一个类似Celsius(t)或者Fahrenheit(t)形式的显示转换操作才能把float64转为对应类型。Celsius(t)和Fahrenheit(t)是类型转换操作,并不是函数调用。类型转换不会改变值的本身,但是会使得语义产生变化。

每一个类型T,都有一个对应的类型转换操作T(x),用于将x转为T类型。当然,必须是两个底层类型相同的,才能这样转换,或者两者都是指向相同底层结构的指针类型。

var c Celsius
var f Fahrenheit
fmt.Println(c == 0) // true
fmt.Println(f >= 0) // true
fmt.Println(c == f) // compile error: type mismatch
fmt.Println(c == Celsius(f)) // true

5.包和文件