More types of Go: Pointers, structs, slices, and maps
§ Pointer to the
Go has Pointers. The pointer holds the memory address of the value.
The type *T is a pointer to a value of type T. Its zero value is nil.
var p *int
Copy the code
The & operator generates a pointer to its operand.
i := 42
p = &i
Copy the code
The * operator represents the underlying value to which the pointer points.
fmt.Println(*p) // Read I through the pointer p
*p = 21 // Set I by the pointer p
Copy the code
This is often referred to as an “indirect reference” or “redirect.”
Unlike C, Go has no pointer operations.
package main
import "fmt"
func main(a) {
i, j := 42.2701
p := &i
fmt.Println(p)
fmt.Println(*p)
*p = 21
fmt.Println(*p)
p = &j
*p /= 37
fmt.Println(j)
}
Copy the code
Output:
0xc00008e000
42
21
73
Copy the code
§ The structure of the body
A struct is a set of fields.
package main
import "fmt"
type Vertex struct {
x int
y int
}
func main(a) {
fmt.Println(Vertex{1.2})}Copy the code
Output:
{1} 2Copy the code
Structure field
Structure fields are accessed using a dot.
package main
import "fmt"
type Vertex struct {
X int
Y int
}
func main(a) {
v := Vertex{1.2}
v.X = 4
fmt.Println(v.X)
}
Copy the code
Output:
4
Copy the code
Structure pointer
Struct fields can be accessed through the struct pointer.
If we have a pointer to the structure p, then we can access its field X through (*p).x. But that’s a long way to go, so the language also allows us to use implicit references, so just write p.X.
package main
import "fmt"
type Vertex struct {
X int
Y int
}
func main(a) {
v := Vertex{1.2}
p := &v
p.X = 1e9
fmt.Println(v)
}
Copy the code
Output:
{1000000000}Copy the code
Struct Literals
Structure grammar assigns a new structure by directly listing the values of the fields.
Use the Name: syntax to list only some fields. (The order of field names is irrelevant.)
The special prefix & returns a pointer to the structure.
package main
import "fmt"
type Vertex struct {
X, Y int
}
var (
v1 = Vertex{1.2}
v2 = Vertex{X: 1}
v3 = Vertex{}
p0 = &Vertex{Y: 1, X: 2})func main(a) {
fmt.Println(v1, v2, v3, *p0)
}
Copy the code
Output:
{1 2} {1 0} {0 0} {2 1}
Copy the code
§ An array of
Type [n]T represents an array with n values of type T.
expression
var a [10]int
The variable A is declared as an array of 10 integers.
package main
import "fmt"
func main(a) {
var a [2]string
a[0] = "Hello"
a[1] = "World"
fmt.Println(a[0], a[1])
fmt.Println(a)
primes := [6]int{2.3.5.7.11.13}
fmt.Println(primes)
}
Copy the code
Output:
Hello World
[Hello World]
[2 3 5 7 11 13]
Copy the code
The length of an array is part of its type, so an array cannot be resized. This may seem like a limitation, but never mind, Go provides a much more convenient way to use arrays.
§ slice
Each array has a fixed size. Slicing, on the other hand, provides a dynamically sized, flexible view of the array elements. In practice, slices are more common than arrays.
Type []T represents a slice of element type T. (Array with no number of elements)
A slice is defined by two subscripts, an upper bound and a lower bound, separated by a colon:
a[low : high]
It chooses a half-open interval that includes the first element but excludes the last.
The following expression creates a slice that contains the elements in a with subscripts 1 through 3:
a[1:4]
package main
import "fmt"
func main(a) {
primes := [6]int{2.3.5.7.11.13}
var s []int = primes[1:4]
fmt.Println(s)
}
Copy the code
Output:
[3 5 7]
Copy the code
Slices are like references to arrays
A slice does not store any data; it simply describes a segment of the underlying array.
Changing the element in the slice modifies the corresponding element in the underlying array.
These changes are observed by slices that share the underlying array with it.
package main
import "fmt"
func main(a) {
names := [4]string{
"A"."B"."C"."D",
}
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)
}
Copy the code
Output:
[A B C D]
[A B] [B C]
[A XXX] [XXX C]
[A XXX C D]
Copy the code
Slice literals
Slice grammar is similar to array grammar with no length.
This is an array grammar:
[3]bool{true, true, false}
This creates the same array as above, and then builds a slice that references it:
[]bool{true, true, false}
package main
import "fmt"
func main(a) {
q := []int{2.3.5.7.11.13}
fmt.Println(q)
r := []bool{true.false.true.false}
fmt.Println(r)
s := []struct {
i int
b bool
}{
{2.true},
{3.false},
{5.true},
}
fmt.Println(s)
}
Copy the code
Output:
[2 3 5 7 11 13]
[true false true false]
[{2 true} {3 false} {5 true}]
Copy the code
Default behavior for slicing
When slicing, you can use its default behavior to ignore the upper and lower bounds.
The lower bound of a slice defaults to 0, and the upper bound is the length of the slice.
For an array of
var a [10]int
For example, the following slices are equivalent:
a[0:10]
a[:10]
a[0:]
a[:]
package main
import "fmt"
func main(a) {
s := []int{2.3.5.7.11.13}
s = s[1:4]
fmt.Println(s)
s = s[:2]
fmt.Println(s)
s = s[1:]
fmt.Println(s)
}
Copy the code
Output:
[3 5 7]
[3 5]
[5]
Copy the code
Length and volume of slices
Slices have length and capacity.
-
The length of a slice is the number of elements it contains.
-
The size of a slice is counted from its first element to the end of its underlying array element.
The length and capacity of slice S can be obtained by len(s) and cap(s).
You can extend a slice’s length by re-slicing it, provided it has sufficient capacity. You can extend a slice’s length by re-slicing it, provided it has sufficient capacity.
To extend it beyond its capacity, there is a Runtime error.
package main
import "fmt"
func main(a) {
s := []int{2.3.5.7.11.13}
printSlice(s)
// Cut the slice so that its length is 0
s = s[:0]
printSlice(s)
// Expand its length
s = s[:4]
printSlice(s)
// Discard the first two values
s = s[2:]
printSlice(s)
// Expand its capacity outward
s = s[2:10]
printSlice(s)
}
func printSlice(s []int) {
fmt.Printf("len=%d\tcap=%d\t %v\n".len(s), cap(s), s)
}
Copy the code
Output:
len=6 cap=6 [2 3 5 7 11 13]
len=0 cap=6 []
len=4 cap=6 [2 3 5 7]
len=2 cap=4 [5 7]
panic: runtime error: slice bounds out of range
goroutine 1 [running]:
main.main()
/Users/example/go/tour/slice-len-cap/src.go:22 +0x483
exit status 2
Copy the code
Nil slice
The zero value of slice is nil.
A nil slice has a length and capacity of 0 and no underlying array.
package main
import "fmt"
func main(a) {
var s []int
fmt.Println(s, len(s), cap(s))
if s == nil {
fmt.Println("nil!")}}Copy the code
Output:
[] 0 0
nil!
Copy the code
Create slices with make
Slices can be created using the built-in make function, which is how dynamic arrays are created.
The make function allocates an array with zero elements and returns a slice that references it:
a := make([]int, 5) // len(a)=5
To specify its size (cap), pass a third argument to make:
b := 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
Copy the code
package main
import "fmt"
func main(a) {
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)
}
Copy the code
Output:
a: 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]
Copy the code
Section of a section
Slices can contain any type, even other slices.
package main
import (
"fmt"
"strings"
)
func main(a) {
// Create a tic-tac-toe board
board := [][]string{[]string{"_"."_"."_"},
[]string{"_"."_"."_"},
[]string{"_"."_"."_"}},// Two players take turns typing an X and an O
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], ""))}}Copy the code
Output:
X _ X
O _ X
_ _ O
Copy the code
Appends elements to slices
Appending new elements to slices is a common operation, for which Go provides the built-in Append function. The built-in function documentation describes this function in detail.
func append(s []T, vs ... T) []T
The first argument s to Append is a slice of element type T, and the remaining values of type T are appended to the end of the slice.
The result of Append is a slice that contains all the elements of the original slice plus the newly added elements.
When s’s underlying array is too small to hold all the given values, it allocates a larger array. The returned slice points to the newly allocated array.
package main
import "fmt"
func main(a) {
var s []int
printSlice(s)
// Add the element to an empty slice
s = append(s, 0)
printSlice(s)
// The slice will grow as needed
s = append(s, 1)
printSlice(s)
You 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)
}
Copy the code
Output:
len=0 cap=0 []
len=1 cap=1 [0]
len=2 cap=2 [0 1]
len=5 cap=6 [0 1 2 3 4]
Copy the code
(To learn more about slicing, read the article Go Slices: Usage and Essence.)
Range
The range form of the for loop traverses slices or maps.
When a for loop is used to traverse a slice, two values are returned for each iteration. The first value is the index of the current element, and the second value is a copy of the element corresponding to that index.
package main
import "fmt"
var pow = []int{1.2.4.8.16.32.64.128}
func main(a) {
for i, v := range pow {
fmt.Printf("2**%d = %d\n", i, v)
}
}
Copy the code
Output:
2 0 = 1 2 * * * * 1 = 2 * 2 * 2 = 4 * * 3 = 8 2 * * 4 = 16 * 2 * 5 = 32 * * 6 = 64 * 2 * 2 = 128Copy the code
Range (continue)
You can ignore the index or value by assigning it to _.
for i, _ := range pow
for _, value := range pow
If you only need the index, ignore the second variable:
for i := range pow
package main
import "fmt"
func main(a) {
pow := make([]int.10)
for i := range pow {
pow[i] = 1 << uint(i) // == 2 ** i
}
for _, value := range pow {
fmt.Printf("%d\n", value)
}
}
Copy the code
Output:
12 4 8 16 32 64 128 256 512Copy the code
Maps (map)
Mapping maps keys to values.
The zero value of the map is nil.
Nil maps have neither keys nor can they be added.
The make function returns a map of the given type and initializes it for standby.
package main
import "fmt"
type Vertex struct {
Lat, Long float64
}
var m map[string]Vertex
func main(a) {
fmt.Println(m)
// m["A"] = Vertex{1.2, 3.4
m = make(map[string]Vertex)
m["B"] = Vertex{
40.05.71.0,
}
fmt.Println(m["B"])}Copy the code
Output:
The map [] {} 40.05-71Copy the code
Grammar of mappings
The grammar of a map is similar to that of a structure, except that it must have a key name.
package main
import "fmt"
type Vertex struct {
Lat, Long float64
}
var m = map[string]Vertex{
"B" : Vertex{1.2},
"C" : Vertex{3.4}},func main(a) {
fmt.Println(m)
}
Copy the code
Output:
map[B:{1 2} C:{3 4}]
Copy the code
Grammar of mappings (cont.)
If the top-level type is just a type name, you can omit it in the elements of the grammar:
package main
import "fmt"
type Vertex struct {
Lat, Long float64
}
var m = map[string]Vertex{
"B": {1.2},
"C": {3.4}},func main(a) {
fmt.Println(m)
}
Copy the code
Output:
map[B:{1 2} C:{3 4}]
Copy the code
Modify the mapping
Insert or modify elements in map M:
m[key] = elem
Get the element:
elem = m[key]
Delete element:
delete(m, key)
Check whether a key exists by double assignment:
elem, ok = m[key]
Ok is true if key is in m; Otherwise, OK is false.
If key is not in the map, then ELEm is the zero value of the mapping element type.
Similarly, when a nonexistent key is read from a map, the result is the zero value of the mapped element type.
Note: If elem or OK is not already declared, you can use a short variable declaration:
elem, ok := m[key]
package main
import "fmt"
func main(a) {
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)
}
Copy the code
Output:
The value: 42
The value: 48
The value: 0
The value: 0 Present? false
Copy the code
Function value
Functions are also values. They can be passed like any other value.
Function values can be used as arguments or return values to a function.
package main
import (
"fmt"
"math"
)
func compute(fn func(float64.float64) float64) float64 {
return fn(3.4)}func main(a) {
hypot := func(x, y float64) float64 {
return math.Sqrt(x*x + y*y)
}
fmt.Println(hypot(5.12))
fmt.Println(compute(hypot))
fmt.Println(compute(math.Pow))
}
Copy the code
Output:
May 13, 81Copy the code
Closure of a function
The Go function can be a closure. A closure is a function value that references a variable outside of its function body. The function can access and assign values to the variables it references; in other words, the function is “bound” to them.
package main
import "fmt"
func adder(a) func(int) int {
sum := 0
return func(x int) int {
sum += x
return sum
}
}
func main(a) {
pos, neg := adder(), adder()
for i := 0; i < 10; i++ {
fmt.Println(
pos(i),
neg(2 -*i),
)
}
}
Copy the code
Output:
0 0 1-2 3-6 6-12 10-20 15-30 21-42 28-56 36-72 45-90Copy the code
The 👆 function adder returns a closure. Each closure is bound to its respective sum variable.
Example: Fibonacci closures
package main
import "fmt"
// fibonacci is a function that returns
// a function that returns an int.
func fibonacci(a) func(a) int {
i, j := 0.1
return func(a) int {
i, j = j, i + j
return i
}
}
func main(a) {
f := fibonacci()
for i := 0; i < 10; i++ {
fmt.Println(f())
}
}
Copy the code
Output:
1 1 2 3 5...Copy the code