写在前面
- 从这部分开始,学习的难度开始增加
- 不像很多编程语言,Go作为新语言并没有抛弃指针
- slice作为一种较新的数据类型,让人感觉很迷
- 这一部分,有c++基础的会比较好理解,涉及到“引用”,“地址”,“深复制和浅复制”,但官方教程对此没作深入理解
- 这段时间接触这份教程,觉得它教给初学者的是感性认识,作为入门的确合适
指针(pointer)
- 教程原话:A pointer holds the memory address of a value.
- 也就是说指针存储了值的内存地址
- 指针的使用参见以下代码:
//一般定义用到了符号*,默认初始值为nil
var p *int //p才是指针
//获取某个变量的地址用到了符号&
v := 22
p = &v
//获取指针所指向内存地址的值也用到符号*
//官方教程也提到了一般称这个行为作提领(dereferencing)或者indirecting
fmt.Println(*p) // *p代表的是指针所存储内存地址里的内容
- 注意,Go中指针不像C或C++,它没有指针算术,这意味着它不能加减
结构体(struct)
- 结构体,可以理解为多种数据类型的集合体,它的用法如下:
//如下定义一个名为Cube的结构体
type Cube struct {
X int
Y int
Z int
}
//初始化一个立方体,使用了初始化符号:=和大括号{}
//依次赋值
square := Cube{2, 2, 2}
/*
还可以通过field:value的方式初始化,这样可以任意顺序
square := Cube{X:2, Y:2, Z:2}
*/
//访问立方体的长宽高(用英文的句号访问)
square.X = 6
square.Y = 3
square.Z = 5
fmt.Println(square.X, square.Y, square.Z)
//同样的,可以定义某个结构体的指针类型
p := &square
//特别地,以下两种写法等价
(*p).X = 8
p.X = 8
type Cube struct {
X, Y, Z int
}
s1 := Cube{X:1} //X的值为1,其他默认为0
s2 := Cube{} //X Y Z都为0
s3 := Cube{X:1, Z:2} //X为1, Y为0,Z为2
数组(Array)
- 类型 [n]T 表示,有n个T类型的数组
- 数组是静态的,n必须为常量,一经定义长度将无法再改变
var my_array [10]int
my_array2 := [3]int{1, 2, 3}
fmt.Println(my_array) // [0 0 0 0 0 0 0 0 0 0]
fmt.Println(my_array2) // [1 2 3]
fmt.Println(my_array2[2]) // 3
slice
- 关于slice常被翻译为切片之类的,我这里觉得有点不妥,干脆不翻译
- 如果读者去过菜市场,那么买冬瓜的时候,通常商家都会准备好一大条冬瓜,问你要多少,
- 然后你可能会用手比画,大概一根手指的长度吧,于是商家一刀切下去,你就得到了冬瓜中一根手指长度的部分(有点流水帐。。。)
- 而slice,就相当于这一刀切下去,得到的一根手指长的部分,冬瓜就是数组(这个比喻不算严谨,但能说明slice并非一片)
//slice的定义和数组相似,它的类型写法是 []T
//slice是一种动态类型,它可以“截取”数组的一部分
//从数组中截取第low位到第high-1位的数据(这个操作其实叫做slicing):array[low:high]
a := [6]int{2,3,5,7,11,13}
var s []int = a[2:5] //此时s为{5,7,11} 可以理解为数学中区间,左闭右开
- int a[] 和 int* a在C/C++中可以说等价的,而Go中的slice是否也可以理解为特殊的指针呢?
a := [6]int{2,3,5,7,11,13}
var s []int = a[2:5]
fmt.Println(a) // {2, 3, 5, 7, 11, 13}
fmt.Println(s) // {5, 7, 11}
s[2] = 8
fmt.Println(a) // {2, 3, 5, 7, 8, 13}
fmt.Println(s) // {5, 7, 8}
//这部分代码的输出结果,基本可以断定,slice的实质是指针,且它有自己配套的运算方法
package main
import "fmt"
func main() {
q := []int{2, 3, 5, 7, 11, 13}
fmt.Println(q)
r := []bool{true, false, true, true, false, true}
fmt.Println(r)
s := []struct {
i int
b bool
}{
{2, true},
{3, false},
{5, true},
{7, true},
{11, false},
{13, true},
}
fmt.Println(s)
}
- 在进行slicing时(即array[low:high]),可以利用low和high的默认值
my_array := [6]int{2,3,5,7,11,13}
s0 := my_array[2:4] // {5,7}
s1 := my_array[:2] // {2,3}
s2 := my_array[4:] //{11,13}
- slice有两个重要属性:len和cap
- len:The length of a slice is the number of elements it contains.
- len就是slice中可访问(为什么是翻译为可访问?)的元素个数
- cap: The capacity of a slice is the number of elements in the underlying array, counting from the first element in the slice.
- cap是指slice背后指向的数组中的容量吗?错。slice每次都要从array中获取low到high - 1位置的访问方式,而cap只是获取其中low到该数组array最后部分的容量
- 对于slice s而言,len和cap可以这样获取:len(s) cap(s)
- 下面是例子
s := []int{2, 3, 5, 7, 11, 13} //s为指向数组{2, 3, 5, 7, 11, 13}的slice
fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s) //len=6,cap=6,{2, 3, 5, 7, 11, 13}
s = s[:0] // {}
fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s) //len=0,cap=6,{}
s = s[:4] //{2,3,5,7}
fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s) //len=4,cap=6,{2,3,5,7}
s = s[2:] //{5,7},这时候的low开始改变了!!
fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s) //len=2,cap=4,{5,7}
- 和指针一样,当slice指向为空,那么slice将为nil值
var s []int
fmt.Println(len(s), cap(s), s)
if s == nil {
fmt.println("nil!")
}
- 还可以使用make函数来创建slice
- 第一个参数是类型
- 第二个参数是len,可以是变量
- 第三个参数时cap,可省略,省略后cap和len相同,可以是变量
s := make([]int, 5) //{0,0,0,0,0}
fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s) //len=5, cap=5, {0,0,0,0,0}
s2 := make([]int, 5, 10) //{0,0,0,0,0}
fmt.Printf("len=%d cap=%d %v\n", len(s2), cap(s2), s2) //len=5, cap=10, {0,0,0,0,0}
- slice还能包含slice,也就是二维slice
- 以下直接给出官方完整例子
package main
import (
"fmt"
"strings"
)
func main() {
// Create a tic-tac-toe board.
board := [][]string{
[]string{"_", "_", "_"},
[]string{"_", "_", "_"},
[]string{"_", "_", "_"},
}
// The players take turns.
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], " "))
}
}
var s []int
s = append(s, 0)
s = append(s, 1, 2)
s = append(s, 3, 4, 5, 6)
- 关于slice的更多内容,还是建议看看https://blog.golang.org/go-slices-usage-and-internals
range
- range一般中文翻译作“范围”
- 会在for语句中使用,要结合for循环来理解
- 每次for循环迭代时,range 数组或者slice,会往后跑一位,返回位置和数值
var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}
for i, v := range pow {
fmt.Printf("2**%d = %d\n", i, v)
}
pow := make([]int, 10)
for i := range pow {
pow[i] = 1 << uint(i) // == 2**i
}
for _, value := range pow {
fmt.Printf("%d\n", value)
}
请发表评论