Go学习笔记(一)

Hello World

Go是一门编译型语言,Go语言的工具链将源代码及其依赖转换成计算机的机器指令。 Go语言提供的工具都通过一个单独的命令 go 调用, go 命令有一系列子命令。

新建 helloworld.go 文件:

1
2
3
4
5
6
7
package main
import "fmt"
func main() {
fmt.Println("Hello, 世界")
}

命令行执行程序:

1
2
$ go run helloworld.go
Hello, 世界

生成二进制文件:

1
$ go build helloworld.go

for 循环

Go语言只有for循环这一种循环语句。for循环有多种形式,其中一种如下所示:

1
2
3
for initialization; condition; post {
// zero or more statements
}

for循环的这三个部分每个都可以省略,如果省略 initialization 和 post ,分号也可以省略:

1
2
3
4
// a traditional "while" loop
for condition {
// ...
}

如果连 condition 也省略了,像下面这样:

1
2
3
4
// a traditional infinite loop
for {
// ...
}

这就变成一个死循环。

for 循环的另一种形式,在某种数据类型的区间(range)上遍历,如字符串或切片。

1
2
3
4
5
6
7
8
func main() {
s, sep := "", ""
for _, arg := range os.Args[1:] {
s += sep + arg
sep = " "
}
fmt.Println(s)
}

每次循环迭代,range 产生一对值;索引以及在该索引处的元素值。 这个例子不需要索引,但 range 的语法要求,要处理元素,必须处理索引。一种思路是把索引赋值给一个临时变量,如 temp ,然后忽略它的值,但Go语言不允许使用无用的局部变量(local variables),因为这会导致编译错误。

Go语言中这种情况的解决方法是用空标识符(blank identifier) ,即 _ (也就是下划线)。空标识符可用于任何语法需要变量名但程序逻辑不需要的时候,例如,在循环里,丢弃不需要的循环索引,保留元素值。

访问控制

如果一个名字是在函数内部定义,那么它的就只在函数内部有效。如果是在函数外部定义,那么将在当前包的所有文件中都可以访问。名字的开头字母的大小写决定了名字在包外的可见性。如果一个名字是大写字母开头的(译注:必须是在函数外部定义的包级名字;包级函数名本身也是包级名字),那么它将是导出的,也就是说可以被外部的包访问,例如fmt包的Printf函数就是导出的,可以在fmt包外部访问。包本身的名字一般总是用小写字母。

变量声明

变量声明的一般语法如下:

1
var 变量名字 类型 = 表达式

其中类型= 表达两个部分可以省略其中的一个。

简短变量声明:

1
变量名称 := 表达式

相较一般的变量声明而言省略了var标识,但是=换成了:=,:=是变量声明语句,=是赋值语句

type 类型声明

type关键词也就是为现有类型定义别名,使得类型名在一些情况下具有意义,也就是便于理解

1
type 类型名字 底层类型

例如:

1
type money float64

money类型的底层虽然是float64,但是它们是不同的数据类型, 因此它们不可以被相互比较或混在一个表达式运算。

对于每一个类型T,都有一个对应的类型转换操作T(x),用于将x转为T类型,只有当两个类型的底层基础类型相同时,才允许这种转型操作,或者是两者都是指向相同底层结构的指针类型,这些转换只改变类型而不会影响值本身。

init() 函数

我们可以用一个特殊的init初始化函数来简化初始化工作。 每个文件都可以包含多个init初始化函数:

1
func init() { /* ... */ }

这样的init初始化函数除了不能被调用或引用外,其他行为和普通函数类似。 在每个文件中的init初始化函数,在程序开始执行时按照它们声明的顺序被自动调用。

iota 常量生成器

常量声明可以使用iota常量生成器初始化, 它用于生成一组以相似规则初始化的常量, 但是不用每行都写一遍初始化表达式。 在一个const声明语句中, 在第一个声明的常量所在的行,iota将会被置为0, 然后在每一个有常量声明的行加一

1
2
3
4
5
6
7
8
9
10
11
type Weekday int
const (
Sunday Weekday = iota
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
)

周日将对应0, 周一为1, 如此等等。相当于枚举,下面是一个更复杂的例子:

1
2
3
4
5
6
7
8
9
10
11
const (
_ = 1 << (10 * iota)
KiB // 1024
MiB // 1048576
GiB // 1073741824
TiB // 1099511627776 (exceeds 1 << 32)
PiB // 1125899906842624
EiB // 1152921504606846976
ZiB // 1180591620717411303424 (exceeds 1 << 64)
YiB // 1208925819614629174706176
)

无类型常量

Go语言的常量有个不同寻常之处。 虽然一个常量可以有任意有一个确定的基础类型,但是许多常量并没有一个明确的基础类型。 编译器为这些没有明确的基础类型的数字常量提供比基础类型更高精度的算术运算;你可以认为至少有256bit的运算精度。 这里有六种未明确类型的常量类型, 分别是无类型的布尔型、 无类型的整数、 无类型的字符、 无类型的浮点数、 无类型的复数、 无类型的字符串。

通过延迟明确常量的具体类型, 无类型的常量不仅可以提供更高的运算精度, 而且可以直接用于更多的表达式而不需要显式的类型转换。

例如,math.Pi无类型的浮点数常量, 可以直接用于任意需要浮点数或复数的地方:

1
2
3
var x float32 = math.Pi
var y float64 = math.Pi
var z complex128 = math.Pi

对于常量面值, 不同的写法可能会对应不同的类型。 例如00.00i\u0000虽然有着相同的常量值, 但是它们分别对应无类型的整数、 无类型的浮点数、 无类型的复数和无类型的字符等不同的常量类型。 同样, truefalse也是无类型的布尔类型, 字符串面值常量是无类型的字符串类型。

重点

  • Go语言原生支持Unicode, 它可以处理全世界任何语言的文本
  • 变量会在声明时直接初始化,因此在Go语言中不存在未初始化的变量
  • ++--都只能放在变量名后面, 因此 --i 也非法
  • for循环三个部分不需括号包围。 大括号强制要求, 左大括号必须和post语句在同一行
  • Go语言不允许使用无用的局部变量(local variables),因为这会导致编译错误
  • Go语言中的NULLnil表示
  • switch语句并不需要显式地在每一个case后写break,语言默认执行完case后的逻辑语句会自动退出
  • 在Go语言里没有指针运算,也就是不能像c语言里可以对指针进行加或减操作。
  • Go语言主要有四种类型的声明语句:varconsttypefunc
  • :=是变量声明语句,=是赋值语句
  • 函数中的变量(局部变量)不一定都在栈上,有些可能在堆上(当然我们可以不用关心这些)
坚持原创技术分享,您的支持将鼓励我继续创作!