漫游Go语言(三)之指针、struct、数组和slice
Go是有指针的,*T
定义一个指向T类型的指针,其缺省值为nil
.1
var p *int
类似地,&
操作符可以取操作数的地址。1
2i := 42
p = &i
而*
操作符也可以取指针指向的值1
2fmt.Println(*p) // 通过指针p读取i值
*p = 21 // 通过指针p更新i值
但与C
不同的是,Go没有指针运算。
struct
struct
是一组域
的集合1
2
3
4
5
6
7
8
9
10
11
12package main
import "fmt"
type Vertex struct {
X int
Y int
}
func main() {
fmt.Println(Vertex{1, 2})
}
struct的域可以通过.
来存取。1
2
3v := Vertex{1, 2}
v.X = 4
fmt.Println(v.X)
struct指针
其定义与基本数据类型无异,但需要注意的是通过它引用域也是使用.
操作符,而无显式的解引用过程。1
2
3
4v := Vertex{1, 2}
p := &v
p.X = 1e9
fmt.Println(v)
struct lteral
struct literal
通过列出struct
域的值来表示一个新建的struct
.你也可以通过name:值
的方式来只指定一个子集。
还可以同时使用&
操作符来直接返回指针。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18package main
import "fmt"
type Vertex struct {
X, Y int
}
var (
v1 = Vertex{1, 2} // has type Vertex
v2 = Vertex{X: 1} // Y:0 is implicit
v3 = Vertex{} // X:0 and Y:0
p = &Vertex{1, 2} // has type *Vertex
)
func main() {
fmt.Println(v1, p, v2, v3)
}
数组
[n]T
表示长度为n类型为T的数组。1
var a [10]int
数组的长度也是它类型的一部分,所以其长度不能改变,这看起来不方便,但不用担心,Go提供一系列操作数组的便捷方法。
(也就是下面提到的slice
了。)
slice
slice指向一组数值,且包含长度。[]T
是一个类型为T的slice.而len(primes)
则返回名为primes的slice的长度。1
2
3
4
5
6
7
8
9
10
11
12package main
import "fmt"
func main() {
primes := []int{2, 3, 5, 7, 11, 13}
fmt.Println("primes ==", primes)
for i := 0; i < len(primes); i++ {
fmt.Printf("primes[%d] == %d\n", i, primes[i])
}
}
slice of slice
slice可包含slice等任意类型。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26package 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[2][0] = "X"
board[1][0] = "O"
board[0][2] = "X"
for i := 0; i < len(board); i++ {
fmt.Printf("%s\n", strings.Join(board[i], " "))
}
}
运行:1
2
3X _ X
O _ _
X _ O
切割slice
slice可被切割s[lo:hi]
代表包含下标lo
至 hi-1
在内的元素,也就是s[lo:lo]
为空,s[lo:lo+1]包含一个元素。
同样可以省略起始或截止位,代表从头或至尾1
2
3
4
5
6
7
8
9
10
11
12
13
14
15package main
import "fmt"
func main() {
s := []int{2, 3, 5, 7, 11, 13}
fmt.Println("s ==", s)
fmt.Println("s[1:4] ==", s[1:4])
// missing low index implies 0
fmt.Println("s[:3] ==", s[:3])
// missing high index implies len(s)
fmt.Println("s[4:] ==", s[4:])
}
运行:1
2
3
4s == [2 3 5 7 11 13]
s[1:4] == [3 5 7]
s[:3] == [2 3 5]
s[4:] == [11 13]
make slice
可使用make
函数来生成slice,它生成一个全0的数组并返回一个引用该数组的slice.1
a := make([]int, 5) // len(a)=5
要指定容量,指定第三个参数即可。1
2
3
4b := make([]int, 0, 5) // len(b)=0, cap(b)=5
b = b[:cap(b)] // len(b)=5, cap(b)=5
b = b[1:] // len(b)=4, cap(b)=4
可见可用cap()方法来得到slice容量1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22package main
import "fmt"
func main() {
a := make([]int, 5)
printSlice("a", a)
b := make([]int, 0, 5)
printSlice("b", b)
c := b[:2]
printSlice("c", c)
d := c[2:5]
printSlice("d", d)
}
func printSlice(s string, x []int) {
fmt.Printf("%s len=%d cap=%d %v\n",
s, len(x), cap(x), x)
}
运行1
2
3
4
5
6a len=5 cap=5 [0 0 0 0 0]
b len=0 cap=5 []
c len=2 cap=5 [0 0]
d len=3 cap=3 [0 0 0]
Program exited.
nil slice
slice的0值(缺省值)为nil
,其长度和容量都是0.1
2
3
4
5
6
7
8
9
10
11package main
import "fmt"
func main() {
var s []int
fmt.Println(s, len(s), cap(s))
if s == nil {
fmt.Println("nil!")
}
}
向slice添加元素
Go为slice提供内置的append方法,其原型为1
func append(s []T, vs ...T) []T
其第一参数为类型为T的slice,后为需要追加的类型为T的值。其返回值为原元素+append
的元素的slice,如果原slice容量不够,会返回一个新建的slice.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24package main
import "fmt"
func main() {
var s []int
printSlice(s)
// append works on nil slices.
s = append(s, 0)
printSlice(s)
// The slice grows as needed.
s = append(s, 1)
printSlice(s)
// We can add more than one element at a time.
s = append(s, 2, 3, 4)
printSlice(s)
}
func printSlice(s []int) {
fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}
运行:1
2
3
4
5
6len=0 cap=0 []
len=1 cap=2 [0]
len=2 cap=2 [0 1]
len=5 cap=8 [0 1 2 3 4]
Program exited.
for range
在for
循环结合range
关键字可以遍历一个slice
.1
2
3
4
5
6
7
8
9
10
11package main
import "fmt"
var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}
func main() {
for i, v := range pow {
fmt.Printf("2**%d = %d\n", i, v)
}
}
range的每次迭代返回两个值,分别是下标和对应值的copy,也可以使用_
来略过下标,或者可以只指定下标。1
2for i := range pow {}
for _, value := range pow {}
slice练习
实现Pic,它返回dy
的slice
,每个元素是dy
的8位无符号整形。当运行这个程序时,它将显示你的图片,将整形数以灰度呈现。
图片任由你选择,有一些有趣的函数如(x+y)/2
,x*y
,x^y
.
(你需要使用循环来为[][]unit8分配每个[]uint8)
(使用unit8(intValue)
来转换数值类型)
注:这个程序只能在golang.org上完成,因为它import
了教程自带的包。
网址为https://tour.golang.org/moretypes/15
题为:1
2
3
4
5
6
7
8
9
10
11package main
import "golang.org/x/tour/pic"
func Pic(dx, dy int) [][]uint8 {
}
func main() {
pic.Show(Pic)
}
我的答案:灰度为x,y坐标积。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20package main
import "golang.org/x/tour/pic"
func Pic(dx, dy int) [][]uint8 {
r := make([][]uint8,dy)
for i := range r {
e := make([]uint8,dy)
for j := range e{
e[j] = uint8(i*j)
}
r[i] = e
}
return r
}
func main() {
pic.Show(Pic)
}
打印出的图案也很有趣:
同样的,建议大家自行试验,PS: x^y的图案也很有趣。
再次附上网址:https://tour.golang.org/moretypes/15