Go 学习笔记
第一章 类型
1.1 变量
Go 语言变量名由字母、数字、下划线组成,其中首个字母不能为数字。
Go 是静态类型语⾔,不能在运⾏期改变变量类型。
使⽤关键字 var 定义变量,⾃动初始化为零值。如果提供初始化值,可省略变量类型,由编译器⾃动推断。
var x int
var y int = 1
var radius float32 = 2.0
var str = "Hello Go"
在函数内部,可⽤更简略的 “:=” ⽅式定义变量。
var func main(){
m:="abc"
}
可以一次定义多个变量。
var x,y,z int //定义全局变量
var m,n = "abc",123
var (
a int
b float32
)
var func main(){
i := "Hello Go" //编译器会将未使⽤的局部变量当做错误=>i declared and not used。
println(x, n, b)
}
注意重新赋值与定义新同名变量的区别。
var (
a int
b float32
)
func main() {
println(&a)
m := "Hello Go"
println(&m)
m, n := "what?", 10 // 重新赋值: 与前 m 在同 层次的代码块中,且有新的变量被定义。
println(&m, n)
a := 12 // 定义新同名变量: 不在同层次代码块。
println(&a)
}
输出结果
0x10b29a8
0xc420039f68
0xc420039f68 10
0xc420039f60
1.2 常量
常量值必须是编译期可确定的数字、字符串、布尔值。
const x, y, z = 1, "Hello Go", false //多常量初始化
const ( //常量组
m = false
n = false
i, j = "abc", 1 //类型自动判断
)
func main() {
const x = 2//未使用局部常量不会引发编译错误
}
在常量组中,如不提供类型和初始化值,那么视作与上 常量相同。
const ( //常量组
m = false
n //n = false
i = 10
j //j = 10
)
常量值还可以是 len、cap、unsafe.Sizeof 等编译期可确定结果的函数返回值。
const (
x = "Hello Go"
y = len(x)
z = unsafe.Sizeof(x)
)
如果常量类型 以存储初始化值,那么不会引发溢出错误。
const (
x byte = 20
y int = 1e20//constant 100000000000000000000 overflows int
)
枚举
关键字 iota 定义常量组中从 0 开始按 计数的 增枚举值。
const (
Sunday = iota //0
Monday //1
Tuesday //2
Wednesday //3
Thursday //4
Friday //5
Saturday //6
)
const (
_ = iota // iota = 0
KB int64 = 1 << (10 * iota) // iota = 1
MB // 与 KB 表达式相同,但 iota = 2 等同于=> MB int64 = 1 << (10 * 2)
GB
TB
)
在同 常量组中,可以提供多个 iota,它们各增 。
const (
A, B = iota, iota << 10 //0,0 << 10
)
如果 iota 增被打断,须显式恢复。
const (
A = iota //1
B //2
C = "c" //c
D //c
E = iota //4 显式恢复。注意计数包含了 C、D 两行 。
F //5
G //6
)
1.3 基本类型
支持八进制、 十六进制,以及科学记数法。标准库 math 定义了各数字类型取值范围。
a, b, c, d := 071, 0x1F, 1e9, math.MinInt16
空指针值 nil
1.4 引用类型
引 类型包括 slice、map 和 channel。它们有复杂的内部结构,除了申请内存外,还需
要初始化相关属性。
内置函数 new 计算类型 ,为其分配零值内存,返回指针。 make 会被编译器翻译 成具体的创建函数,由其分配内存和初始化成员结构,返回对象 指针。
number := []int{0, 0, 0}
number[1] = 10
x := make([]int, 3) // len = 3 slice.c: runtime·makeslice
x[1] = 10
y := new([]int)
y[0] = 10 // Error: invalid operation: y[1] (index of type *[]int)
1.5 类型转换
不⽀持隐式类型转换,即便是从窄向宽转换也不⾏。
var x byte = 10
//var m int = x //cannot use x (type byte) as type int in assignment
y := int(x)
同样不能将其他类型当 bool 值使⽤。
var x = 1
if x {//non-bool x (type int) used as if condition
println("true")
}
1.6 字符串
字符串是不可变值类型,内部⽤指针指向 UTF-8 字节数组。
• 默认值是空字符串 “”。
• ⽤索引号访问某字节,如 s[i]。
• 不能⽤序号获取字节元素指针,&s[i] ⾮法。
• 不可变类型,⽆法修改字节数组。
• 字节数组尾部不包含 NULL。
// string is the set of all strings of 8-bit bytes, conventionally but not
// necessarily representing UTF-8-encoded text. A string may be empty, but
// not nil. Values of string type are immutable.
type string string
使⽤索引号访问字符 (byte)。
var str = "Hello Goland"
println(str[0] == 72, str[1], str[len(str)-1])
输出
true 101 100
连接跨⾏字符串时,”+” 必须在上⼀⾏末尾,否则导致编译错误。
var str = "Hello " + " Goland"
s2 := "Hello, "
+ "World!" //invalid operation: + untyped string
println(str)
println(s2)
⽀持⽤两个索引号返回⼦串。⼦串依然指向原字节数组,仅修改了指针和⻓度属性。
str := "Hello " + " Goland"
str1 := str[1:] //ello Goland
str2 := str[:6] //Hello
str3 := str[2:5] //llo
println(str1)
println(str2)
println(str3)
单引号字符常量表⽰ Unicode Code Point,⽀持 \uFFFF、\U7FFFFFFF、\xFF 格式。
对应 rune(int32) 类型,UCS-4。
func main() {
fmt.Printf("%T\n", 'a')
var c1, c2 rune = '\u6211', '们'
println(c1 == '我', string(c2) == "\xe4\xbb\xac")
}
输出
int32
true true
要修改字符串,可先将其转换成 []rune 或 []byte,完成后再转换为 string。⽆论哪种转
换,都会重新分配内存,并复制字节数组。
str := "Hello Goland"
number := "110"
s := []byte(str)
phone := []rune(number)
s[0] = 'F'
phone[len(number)-1] = '9'
println(string(s))
println(string(phone))
输出
Fello Goland
119
⽤ for 循环遍历字符串时,也有 byte 和 rune 两种⽅式。
str := "Hello"
for i := 0; i < len(str); i++ { //byte
fmt.Printf("str[%d] = %c \n", i, str[i])
}
for _, value := range str { //rune
fmt.Printf("%c\n", value)
}
输出
str[0] = H
str[1] = e
str[2] = l
str[3] = l
str[4] = o
H
e
l
l
o
1.7 指针
⽀持指针类型 T,指针的指针 **T,以及包含包名前缀的 \
type User struct {
name string
}
user := User{"小强"}
userIp := &user
var ipStr *User
ipStr = &user
println(ipStr)
println(userIp)
fmt.Printf("name=%s\n", userIp.name)
输出
0xc420039f58
name=小强
0xc420039f58
不能对指针做加减法等运算。
number := 1234
p := &number
p++ //invalid operation: p++ (non-numeric type *int)
可以在 unsafe.Pointer 和任意类型指针间进⾏转换。
x := 0x12345678
p := unsafe.Pointer(&x) // *int -> Pointer
n := (*[4]byte)(p) // Pointer -> *[4]byte
for i := 0; i < len(n); i++ {
fmt.Printf("%X ", n[i])
}
输出
78 56 34 12 0xc420039f48
返回局部变量指针是安全的,编译器会根据需要将其分配在 GC Heap 上。
func test() *int {
x := 100
return &x // 在堆上分配 x 内存。但在内联时,也可能直接分配在目标栈。
}
将 Pointer 转换成 uintptr,可变相实现指针运算。
func main() {
d := struct {
s string
x int
}{"abc", 100}
p := uintptr(unsafe.Pointer(&d)) // *struct -> Pointer -> uintptr
p += unsafe.Offsetof(d.x) // uintptr + offset
p2 := unsafe.Pointer(p) // uintptr -> Pointer
px := (*int)(p2) // Pointer -> *int
*px = 200 // d.x = 200
fmt.Printf("%#v\n", d)
}
输出
struct { s string; x int }{s:"abc", x:200}
注意:GC 把 uintptr 当成普通整数对象,它⽆法阻⽌ “关联” 对象被回收。