指针
& 操作符会生成一个指向其操作数的指针
i := 42
p = &i
类型 *T 是指向 T 类型值的指针。其零值为 nil
var p *int
* 操作符表示指针指向的底层值。
fmt.Println(*p) // 通过指针 p 读取 i
*p = 21 // 通过指针 p 设置 i
package main
import "fmt"
func main(){
i, j := 42, 2701
p := &i // 指向 i
fmt.Println(*p) // 通过指针读取 i 的值
*p = 21 // 通过指针设置 i 的值
fmt.Println(i) // 查看 i 的值
p = &j // 指向 j
*p = *p / 37 // 通过指针对 j 进行除法运算
fmt.Println(j) // 查看 j 的值
}
结构体
一个结构体(struct)就是一组字段(field)
package main
import "fmt"
type Vex struct{
x int
y int
}
func main(){
fmt.Println(Vex{1,2})
}
-
结构体字段
结构体字段使用点号来访问
package main
import "fmt"
type Vex struct{
x int
y int
}
func main(){
vex := Vex
p := vex.x
fmt.Println(Vex{1,2})
}
-
结构体指针
结构体字段可以通过结构体指针来访问。
语言也允许我们使用隐式间接引用,直接写 p.X 就可以。
import "fmt"
type Vex struct{
x int
y int
}
func main(){
vex := Vex
p := vex.x
a := &vex.y
a.x = 1
fmt.Println(Vex{1,2})
}
-
结构体文法
结构体文法通过直接列出字段的值来新分配一个结构体
使用 Name: 语法可以仅列出部分字段。(字段名的顺序无关。)
特殊的前缀 & 返回一个指向结构体的指针。
package main
import "fmt"
type Vex struct {
X, Y int
}
func main(){
v1 = Vex{1,2}
v2 = Vex{X:1} //Y被隐式赋值为0
v3 = Vex{} // X,Y为0
p = &Vex{}
}
数组
类型 [n]T 表示拥有 n 个 T 类型的值的数组。
表达式
var a [10]int
package main
import "fmt"
func main(){
var b [10]int
b[0] = 1
b[1] = 2
primes := [6]int{2, 3, 5, 7, 11, 13}
}
切片
每个数组的大小都是固定的。而切片则为数组元素提供动态大小的、灵活的视角。在实践中,切片比数组更常用。
类型 []T 表示一个元素类型为 T 的切片
一个半开区间,包括第一个元素,但排除最后一个元素。
a[low : high]
package main
import "fmt"
func main() {
primes := [6]int{2, 3, 5, 7, 11, 13}
var s []int = primes[1:4] // 范围1-3
fmt.Println(s)
}
-
切片就像数值的引用
切片并不存储任何数据,它只是描述底层数组中的一段。
对切片的更改会修改其底层数组中对应的元素。
package main
import "fmt"
func main() {
names := [4]string{
"John",
"Paul",
"George",
"Ringo",
}
fmt.Println(names)
a := names[0:2]
b := names[1:3]
fmt.Println(a, b)
b[0] = "XXX"
fmt.Println(a, b)
fmt.Println(names)
}
-
切片文法
切片文法类似于没有长度的数组文法。
[]bool{true, true, false}
package main
import "fmt"
func main() {
q := []int{2, 3, 5, 7, 11, 13}
fmt.Println(q)
}
-
切片的默认行为
在进行切片时,你可以利用它的默认行为来忽略上下界。
切片下界的默认值为 0,上界则是该切片的长度。
a[0:10]
a[:10]
a[0:]
a[:]
package main
import "fmt"
func main(){
s := []int{2,3,4}
s = s[:2]
s = s[:]
}
-
nil切片
切片的零值是 nil。
nil 切片的长度和容量为 0 且没有底层数组。
package main
import "fmt"
func main() {
var s []int
if s == nil {
fmt.Println("nil!")
}
}
-
用 make 创建切片
make 函数会分配一个元素为零值的数组并返回一个引用了它的切片
golang 分配内存主要有内置函数new和make,make只能为slice, map, channel分配内存,并返回一个初始化的值。
make(map[string]string)
make([]int, 2)
make([]int, 2, 4)
-
切片的切片
package main
import (
"fmt"
"strings"
)
func main() {
board := [][]string{
[]string{"_", "_", "_"},
[]string{"_", "_", "_"},
[]string{"_", "_", "_"},
}
board[0][0] = "X"
board[2][2] = "O"
board[1][2] = "X"
board[1][0] = "O"
board[0][2] = "X"
for i := 0; i < len(board); i++ {
fmt.Printf("%s\n", strings.Join(board[i], " "))
}
}
-
向切片追加元素
Go 提供了内建的 append 函数。
func append(s []T, vs ...T) []T
append 的第一个参数 s 是一个元素类型为 T 的切片,其余类型为 T 的值将会追加到该切片的末尾。
当 s 的底层数组太小,不足以容纳所有给定的值时,它就会分配一个更大的数组。返回的切片会指向这个新分配的数组。
package main
import "fmt"
func main(){
var s []int
s = append(s,0)
s = append(s , 1)
s = append(s,2,3,4)
}
Range
for 循环的 range 形式可遍历切片或映射。
当使用 for 循环遍历切片时,每次迭代都会返回两个值。第一个值为当前元素的下标,第二个值为该下标所对应元素的一份副本
package main
import "fmt"
var pow : = {1,2,3,4,5}
func main(){
for i,v := range pow{
fmt.Printf("2**%d = %d\n", i, v)
}
}
可以将下标或值赋予 _ 来忽略它。
for i, _ := range pow
for _, value := range pow
// 忽略第二个变量
for i := range pow
映射 (map)
映射将键映射到值。
映射的零值为 nil 。nil 映射既没有键,也不能添加键。
make 函数会返回给定类型的映射,并将其初始化备用。
package main
import "fmt"
type Vex struct{
x,y float64
}
var m map[string]Vex
func main(){
m = make(map[string]Vex)
m["Bell Labs"] = Vex{
40.68433, -74.39967,
}
fmt.Println(m["Bell Labs"])
}
-
映射的文法
映射的文法与结构体相似,不过必须有键名
package main
import "fmt"
type Vex struct{
a, b float64
}
var m = map[string]Vex{
"Hi":{
43.1, 23.1
}
}
func main(){
}
-
修改映射
通过双赋值检测某个键是否存在:
若 key 在 m 中,ok 为 true ;否则,ok 为 false。
若 key 不在映射中,那么 elem 是该映射元素类型的零值。
elem, ok = m[key]
package main
import "fmt"
func main() {
m := make(map[string]int)
m["Answer"] = 42
fmt.Println("The value:", m["Answer"])
m["Answer"] = 48
fmt.Println("The value:", m["Answer"])
delete(m, "Answer")
fmt.Println("The value:", m["Answer"])
v, ok := m["Answer"]
fmt.Println("The value:", v, "Present?", ok)
}
函数值
函数也是值。它们可以像其它值一样传递。
函数值可以用作函数的参数或返回值。
package main
import "fmt"
func comp(fn func(int,int) int) int{
return fn(3,4)
}
func main(){
hx := func(x ,y int) int{
return x+y
}
fmt.Println(hx(3,4))
fmt.Println(comp((hx))
}
函数的闭包
package main
import "fmt"
func add () func(int) int {
sum := 0
return func(x int) int{
sum += x
return sum
}
}
func main(){
pos, neg := adder(), adder()
for i := 0; i < 10; i++ {
fmt.Println(
pos(i),
neg(-2*i),
)
}
}