定义变量
Go语言定义变量有多种方式。
- 使用
var
关键字是Go最基本的定义变量方式。
// 定义一个名称为"variableName",类型为"type"的变量
var variableName tpye
- 定义多个变量
// 定义三个类型都是"type"的变量
var vname1, vname2, vname3 type
- 定义变量并初始化值
// 初始化"variableName"的变量为"value"值,类型是"type"
var variableName type = value
- 同时初始化多个变量
/*
定义三个类型都是"type"的变量,并且分别初始化为相应的值
vname1为v1,vname2为v2,vname3为v3
*/
var vname1, vname2, vname3 type = v1, v2, v3
- 忽略类型声明
/*
定义三个变量,它们分别初始化为相应的值
vname1为v1,vname2为v2,vname3为v3
然后Go会根据其相应值的类型来帮助初始化
*/
var vname1, vname2, vname3 = v1, v2, v3
- 简短声明
/*
定义三个变量,它们分别初始化为相应的值
vname1为v1,vname2为v2,vname3为v3
编译器会根据初始化的值自动推导出相应的类型
*/
vname1, vname2, vname3 := v1, v2, v3
使用简短声明,只能在函数内部使用,在函数外部使用则无法编译通过,一般使用
var
来定义全局变量。
_
变量
_
(下划线)是个特殊的变量名,任何赋予它的值都会被丢弃。
_, b := 34, 35
35的值会赋予b
,34会被丢弃。
Go
对于已声明但未使用的变量会在编译阶段报错。
package main
func main() {
var i int
}
上述代码中,声明了i
却未使用,编译时会报错。
常量
所谓常量,也就是在程序编译阶段就确定下来的值,而程序在运行时无法改变的值。在Go程序中,常量可定义为数值、布尔值或字符串等类型。
const constantName = value
// 如果需要,也可以明确指定常量的类型:
const Pi float32 = 3.1415926
一些常量声明的例子:
const Pi = 3.1415926
const i = 10000
const MaxThread = 10
const prefix = "astaxie_"
内置基础类型
Boolean
在Go中,布尔值的类型为bool
,值是true
或false
,默认为false
。
var isActive bool // 全局变量声明
var enabled, disabled = true, false // 忽略类型的声明
func test() {
var available bool // 一般声明
valid := false // 简短声明
abailable = true // 赋值操作
}
数值类型
整数类型有无符号和带符号两种。Go同时支持int
和uint
,这两种类型的长度相同,但具体长度取决于不同编译器的实现。Go里面也有直接定义好位数的类型:rune
, int8
, int16
, int32
, int64
和byte
, uint8
, uint16
, uint32
,uint64
。其中rune
是int32
的别称,byte
是uint8
的别称。
需要注意的一点是,这些类型的变量之间不允许互相赋值或操作,不然会在编译时引起编译器报错。
如下的代码会产生错误:invalid operation: a + b (mismatched types int8 and int32)
var a int8
var b int32
c:=a + b
浮点数的类型有float32
和float64
两种(没有float
类型),默认是float64
。
Go语言支持复数,默认类型是complex128
(64位实数+64位虚数)。支持较小一些的复数,complex64
(32位实数+32位虚数)。
复数的形式为RE + IMi
,其中RE
是实数部分,IM
是虚数部分,而最后的i
是虚数单位。
var c complex64 = 5+5i
//output: (5+5i)
fmt.Printf("Value is: %v", c)
字符串
Go中的字符串都是采用UTF-8
字符集编码。字符串是用一对双引号(""
)括起来定义,它的类型是string
。
var frenchHello string // 声明变量为字符串的一般方法
var emptyString string = "" // 声明了一个字符串变量,初始化为空字符串
func test() {
no, yes, maybe := "no", "yes", "maybe" // 简短声明,同时声明多个变量
japaneseHello := "Konichiwa" // 同上
frenchHello = "Bonjour" // 常规赋值
}
在Go中字符串是不可变的,例如下面的代码编译时会报错:cannot assign to s[0]
。
var s string = "hello"
s[0] = 'c'
修改字符串可以使用如下代码:
s := "hello"
c := []byte(s) // 将字符串 s 转换为 []byte 类型
c[0] = 'c'
s2 := string(c) // 再转换回 string 类型
fmt.Printf("%s\n", s2)
Go中可以使用+
操作符来连接两个字符串:
s := "hello,"
m := " world"
a := s + m
fmt.Printf("%s\n", a)
修改字符串也可写为:
s := "hello"
s = "c" + s[1:] // 字符串虽不能更改,但可进行切片操作
fmt.Printf("%s\n", s)
声明多行字符串,可以使用如下方式来声明:
m := `hello
world`
`括起来的字符串为Raw字符串,即字符串在代码中的形式就是打印时的形式,它没有字符转义,换行也将原样输出。例如本例中会输出:
hello
world
错误类型
Go内置有一个error
类型,专门用来处理错误信息,Go的package
里面还专门有一个包errors
来处理错误:
err := errors.New("emit macho dwarf: elf header corrupted")
if err != nil {
fmt.Print(err)
}
Go数据底层的存储
map
map
也就是Python
中字典的概念,它的格式为map[keyType]valueType
。
我们看下面的代码,map
的读取和设置也类似slice
一样,通过key
来操作,只是slice
的index
只能是int
类型,而map
多了很多类型,可以是int
,可以是string
及所有完全定义了==
与!=
操作的类型。
// 声明一个key是字符串,值为int的字典,这种方式的声明需要在使用之前使用make初始化
numbers := make(map[string]int)
// 另一种map的声明方式
var numbers map[string]int
numbers["one"] = 1 //赋值
numbers["tow"] = 2 //赋值
numbers["three"] = 3 //赋值
fmt.Println("第一个数字是: ", numbers["one"]) // 读取数据
fmt.Println("第二个数字是: ", numbers["tow"]) // 读取数据
fmt.Println("第三个数字是: ", numbers["three"]) // 读取数据
map
与表格一样,左边列是key
,右边列是值
使用map
过程中需要注意的几点:
map
是无序的,每次打印出来的map
都会不一样,它不能通过index
获取,而必须通过key
获取map
的长度是不固定的,也就是和slice
一样,也是一种引用类型- 内置的
len
函数同样适用于map
,返回map
拥有的key
的数量 map
的值可以很方便的修改,通过numbers["one"]=11
可以很容易的把key
为one
的字典值改为11
map
和其他基本型别不同,它不是thread-safe
,在多个go-routine
存取时,必须使用mutex lock
机制map
的初始化可以通过key:val
的方式初始化值,同时map
内置有判断是否存在key
的方式
通过delete
删除map
的元素:
// 初始化一个字典
rating := map[string]float32{"C":5, "Go":4.5, "Python":4.5, "C++":2 }
// map有两个返回值,第二个返回值,如果不存在key,那么ok为false,如果存在ok为true
csharpRating, ok := rating["C#"]
if ok {
fmt.Println("C# is in the map and its rating is ", csharpRating)
} else {
fmt.Println("We have no rating associated with C# in the map")
}
delete(rating, "C") // 删除key为C的元素
map
也是一种引用类型,如果两个map
同时指向一个底层,那么一个改变,另一个也相应的改变:
m := make(map[string]string)
m["Hello"] = "Bonjour"
m1 := m
m1["Hello"] = "Salut" // 现在m["hello"]的值已经是Salut了
make、new操作
make
用于内建类型(map
、slice
和channel
)的内存分配。new
用于各种类型的内存分配。
内建函数new
本质上说跟其它语言中的同名函数功能一样:new(T)
分配了零值填充的T类型的内存空间,并且返回其地址,即一个*T
类型的值。用Go的术语说,它返回了一个指针,指向新分配的类型T的零值。有一点非常重要:
new 返回指针。
内建函数make(T, args)
与new(T)
有着不同的功能,make
只能创建slice
、map
和channel
,并且返回一个有初始值(非零)的T
类型,而不是*T
。本质来讲,导致这三个类型有所不同的原因是指向数据结构的引用在使用前必须被初始化。例如,一个slice
,是一个包含指向数据(内部array
)的指针、长度和容量的三项描述符;在这些项目被初始化之前,slice
为nil
。对于slice
、map
和channel
来说,make
初始化了内部的数据结构,填充适当的值。
make 返回初始化后的(非零)值。