当前位置:网站首页>Go basic programming: array, slice, string

Go basic programming: array, slice, string

2020-11-10 16:01:10 A night of smoke and clouds

The difference between a value type and a reference type

Value type : Use variables to point to The value of memory , Memory allocation is usually in the stack , In case of assignment and parameter transfer, the value of the data is ( There may be multiple data , For example, the array includes data fields and length ) Make a copy together .
Reference type : Reference type data uses variables Memory address , The location of the first word in memory , This memory address is called a pointer , The pointer is actually stored in another word .

Why put arrays 、 section 、 There are three types of string ? Because their data structures are closely related , At the bottom, their memory structure is the same , It's just that they behave differently at the top because of grammar . Arrays and strings are value types , Slicing is a reference type .

Array

The array is made up of The same kind of data Element composition Fix A sequence of lengths , An array consists of zero or more elements . Arrays are value type data , The length cannot be changed , Content of the variable , The array itself is copied as a whole . Because arrays are fixed length , Often used less, more use of slices .

Definition
var a [4]int                   // Define the length 4, Element type is int, The value is   0 0 0 0
var b = [5]int{1, 2, 3, 4}     // Define the length 5, Element type is int, The value is   1 2 3 4 0
var c = [...]int{1, 2, 3, 4}   // Define the length 4, Element type is int, The value is   1 2 3 4
var d = [4]int{1: 2, 3}        // Define the length 4, Element type is int, The value is   0 2 3 0
var e [4]int = [4]int{1: 2, 3} // Define the length 4, Element type is int, The value is   0 2 3 0
var f  = [2]string{"hello","world"} // Define the length 2, Element type is string, The value is   "hello","world"

Memory structure , Here's the array b Memory structure , Relatively simple :

var b = [5]int{1, 2, 3, 4}

image.png

Basic operation

// Array subscript is from 0 At the beginning . If the subscript is outside the legal range of the array , Trigger access cross-border , Meeting panic
var arr = [5]int{1, 2, 3, 4} 
fmt.Println(arr)       // Print all  [1 2 3 4 0]
fmt.Println(arr[1])  // Print the subscript as 1( The second element ) 2
fmt.Println(arr[5])  //invalid array index 5 (out of bounds for 5-element array)

for Loop to iterate the array

var arr = [5]int{1, 2, 3, 4}
for range arr {
    fmt.Println("hello world")
}
//1、hello hello hello hello hello 

for k := range arr {
    fmt.Println(k)
}
//2、 Print subscript :
//0 1 2 3 4

for k, v := range arr {
    fmt.Println(k, v)
}
//3、key=0,val=1 key=1,val=2 key=2,val=3 key=3,val=4 key=4,val=0
// Don't want to print k, It can be used  "_"  Instead of k, Said to ignore k

for i := 0; i < len(arr); i++ {
    fmt.Println(i, arr[i])
}
//4、key=0,val=1 key=1,val=2 key=2,val=3 key=3,val=4 key=4,val=0 

Operations between two arrays
Arrays are value type data , Variable points to all data in memory , Including the length . That is, the length of the array is also part of the array , Therefore, arrays of different lengths cannot be assigned directly , The size can't be compared , No problem with the same length .

// When the array length is the same :
var a = [5]int{1, 2, 3, 4}
var b = [5]int{}
b = a
fmt.Println(b) //[1 2 3 4 0]
fmt.Println(b != a) //false

// When the array length is different :
var a = [5]int{1, 2, 3, 4}
var b = [6]int{}
b = a //cannot use a (type [5]int) as type [6]int in assignment
fmt.Println(b != a) //invalid operation: b != a (mismatched types [6]int and [5]int)

// Want to put a The value of b It can only be changed by subscript 
for i := 0; i < len(a); i++ {
    b[i] = a[i]
}
fmt.Println(b) //[1 2 3 4 0 0]

Multidimensional arrays
Multidimensional arrays are arrays of arrays , The array can be made into an element type that we often use , It's easy to understand .
Statement
Two dimensional array declaration : var v_name [ That's ok ][ Column ]v_type

var a = [3][2]int{{}, {}, {}} //[[0 0] [0 0] [0 0]]
var b = [3][2]int{[2]int{}, [2]int{}, [2]int{}} //[[0 0] [0 0] [0 0]]
var c = [3][2]int{{1}, {2}, {3}} //[[1 0] [2 0] [3 0]]
var d = [3][2]int{{1, 2}, {2, 2}, {3, 2}} //[[1 2] [2 2] [3 2]]

operation

 assignment 
var arr [3][2]int
fmt.Println(arr) //[[0 0] [0 0] [0 0]]
arr[0][1] = 2
arr[2][0] = 3
fmt.Println(arr) //[[0 2] [0 0] [3 0]]

For two-dimensional arrays , If you think of each row as an element, it's a one-dimensional array , The basic operation is not different from one-dimensional array , Other multidimensional arrays are similar , I won't say it .

character string

character string (string) yes UTF-8 A sequence of characters ( The character is ASCII Inside and outside length 1 byte , other 2~4 Bytes , Such as Chinese 3 Bytes ), It's unchangeable , Read only sequence , Can contain any character . That is, it cannot be changed after it is created , It's essentially a fixed length array of bytes .
Go The string is at the bottom reflect.StringHeader structure :

type StringHeader struct {
   Data uintptr
   Len int
}

String composition consists of two parts : Part of it is a pointer to the underlying array of bytes , Part of it is byte length . When a string is copied, it is actually a copy reflect.StringHeader Structure , Does not copy the underlying byte array .
Here's the string hello world Memory structure :
image.png

Definition
Go Use double quotes "" Or back quote ` The wrapped representation is string content , Be careful not to use single quotation marks ', Single quotation marks are bytes (byte), namely UTF-8 Corresponding code

var str string
var str1 = "hello"
var str2 string = "world"
str3 := "hello world"
fmt.Println(str) //
fmt.Println(str1) //hello
fmt.Println(str2)//world
fmt.Println(str3)//hello world

Initial value
Go The initial value of the string is an empty string "", Be careful " " Not an empty string , This represents a space string , Take up a byte , Corresponding ASCLL The code 00100000B(32H).
Special characters
Some characters use \ Add common characters to express special meanings , Like a new line

  • \n: A newline
  • \r: A carriage return
  • \t:tab key
  • \u or U:Unicode character
  • \\: The backslash itself

Traverse
Because the bottom layer is an array , You can use for range To traverse , It should be noted that the size of each character is not necessarily the same ,key It doesn't have to be continuous , Such as :

str := " China abc"
for k, v := range str {
    fmt.Println(k, v)
}
//0 20013
//3 22269
//6 97
//7 98
//8 99

Immutability
This immutability makes it easy to think of the string as a constant , Once you declare that the entire life cycle cannot change . This cannot be changed by changing the key subscript :

str := "abcdef"
fmt.Println(str[1]) // Access by subscript ( read ): 98
str[1] = "h" //  Change... By subscript ( Write ):cannot assign to str[1] 

You may wonder if you can change the string :

str := "abcdef"
fmt.Println(str) //abcdef
str = "hello world"
fmt.Println(str) //hello world

This change is to change the underlying pointer to the array , Is to re open a memory address to save new data , Discard the original pointer array .

package main

import (
    "fmt"
    "reflect"
    "unsafe"
)

func main() {
    str := "hello "
    fmt.Println(stringAddr(str)) //4999457
    str = "world"
    fmt.Println(stringAddr(str)) //4999111
}

func stringAddr(s string) uintptr {
    return (*reflect.StringHeader)(unsafe.Pointer(&s)).Data
}

String concatenation

  1. + No. splices , There will be expenses

    str1 := "hello "
    str2 := "world"
    str := str1 + str2
    fmt.Println(str) //hello world
  2. fmt Print function under package , The efficiency is not high

    str1 := "hello "
    str2 := "world"
    str := fmt.Sprintf("%s%s", str1, str2)
    fmt.Println(str) //hello world
  3. strings.Join Represents the string array to be concatenated with , In the case of an existing array , It's going to be very efficient , If not, the efficiency is not high .

    str1 := "hello "
    str2 := "world"
    str3 := []string{str1, str2}
    str := strings.Join(str3, "")
    fmt.Println(str) //hello world
  4. Byte buffer bytes.Buffer The performance of this method is much better than the above

    str1 := "hello "
    str2 := "world"
    var by bytes.Buffer
    by.WriteString(str1)
    by.WriteString(str2)
    fmt.Println(by.String())
  5. strings.Builder The performance and bytes.Buffer equally

    str1 := "hello "
    str2 := "world"
    var by strings.Builder
    by.WriteString(str1)
    by.WriteString(str2)
    fmt.Println(by.String())

although bytes.Buffer and strings.Builder Best performance , Officials also suggested , but + Easier and more convenient , Often used the most .

length
Although the substructure has a length , But strings don't include length , To get the string length and array 、 Slicing also uses built-in functions len() obtain . The range of a string is determined by its length , Not special characters \0 .

section

Slices can be seen as The dynamic array , The length is variable , So length is not part of the type . section (slice) Is a reference to a continuous fragment of an array , So slicing is a reference type ( So it's more like C/C++ Array type in , perhaps Python Medium list type ), This fragment can be an entire array , It can also be a subset of some items identified by the start and end indexes , It should be noted that , The item whose index is terminated is not included in the slice .
Go The string is at the bottom reflect.SliceHeader structure :

type SliceHeader struct {
   Data uintptr
   Len int
   Cap int
}

reflect.SliceHeader The structure consists of three parts : Part of it is the slice data pointer , The other part is the length of the slice , The last part is the slice volume .
Memory structure
Definition

var arr1 []int // nil section 
var arr2 []int = []int{1, 2}//  The initial value is [1 2]
var arr3 = []int{1, 2, 3}// The initial value is [1 2 3]
var arr4 = arr3[1:]//arr3 Part of   The initial value is [2 3]
var arr5 = make([]int, 3) //  Yes 3 Slices of elements , len and cap All for 3
var arr6 = make([]int, 2, 3)//  Yes 2 Slices of elements , len by 2, cap by 3

Initial value

The initial value is nil, Cannot be equivalent to an empty string ""、 Boolean value false And numerical 0, These are different types of data , You can't switch between them .

Basic operation
Slices are dynamic arrays , Array operation , Slices can .
To obtain the length of the 、 Capacity
The length is the actual length of the slice , The capacity is the maximum capacity of the slice .0 <= len(s) <= cap(s). When adding data to a slice exceeds the slice capacity , Will lose the original slice data section , Open up a space to store new data , The size of the opening space is the maximum capacity of the previous slice 2 times .

package main

import (
    "fmt"
    "reflect"
    "unsafe"
)

func main() {
    var arr1 = []int{1, 2, 3}
    var arr2 = make([]int, 2, 3)
    fmt.Println(len(arr2), cap(arr2)) //2 3
    fmt.Println(sliceAddr(arr1))      //824634252976
    fmt.Println(len(arr1), cap(arr1)) //3 3
    arr1 = append(arr1, 4)
    fmt.Println(len(arr1), cap(arr1)) //4 6
    fmt.Println(sliceAddr(arr1))      //824634253360
}
// Get slice data address 
func sliceAddr(s []int) uintptr {
    return (*reflect.SliceHeader)(unsafe.Pointer(&s)).Data
}

Single element operation

package main

import (
    "fmt"
)

func main() {

    var arr = []int{1, 2, 3}
    // Print the subscript as 1 The elements of :
    fmt.Println(arr[1]) // 2
    // Get all the elements :
    fmt.Println(arr) //[1 2 3]

    //  Change the subscript to 2 The elements of 
    arr[2] = 5
    fmt.Println(arr[2]) //5

    // Don't cross the border 
    fmt.Println(arr[3]) //panic: runtime error: index out of range

}

Add operation
Built in functions append() To add an element to the end of a slice :

package main

import (
    "fmt"
)

func main() {

    var arr = []int{1, 2, 3}
    fmt.Println(arr) //[1 2 3]
    //  Add an element 
    arr = append(arr, 4)
    fmt.Println(arr) //[1 2 3 4]
    //  newly added 2 Elements 
    arr = append(arr, 5, 6)
    fmt.Println(arr) //[1 2 3 4 5 6]
    //  Add multiple elements , Grammatical sugar (arr1...)  Express the arr1  From the beginning to the end, we assign values one by one 
    var arr1 = []int{20, 30}
    arr = append(arr, arr1...)
    fmt.Println(arr) //[1 2 3 4 5 6 20 30]

}

Copy
Shallow copy : Copy memory address , Modifying the data has an effect on the original data
Deep copy : Copy this value , Memory address is different , Modifying the data does not affect the original data

package main

import (
    "fmt"
    "reflect"
    "unsafe"
)

func main() {

    var arr = []int{1, 2, 3, 4, 5, 6, 20, 30}
    fmt.Println(arr) //[1 2 3 4 5 6 20 30]
------------------------- Shallow copy ----------------------------
    //  assignment 
    arr1 := arr
    fmt.Println(arr1) //[1 2 3 4 5 6 20 30]
    //  Intercept from subscript 2 And the rest 
    arr1 = arr[2:]
    fmt.Println(arr1) //[3 4 5 6 20 30]
    //  Intercept from subscript 5 Front part 
    arr1 = arr[:5]
    fmt.Println(arr1) //[1 2 3 4 5]
    //  Intercept from subscript 2-5 Part of 
    arr1 = arr[2:5]
    fmt.Println(arr1) //[3 4 5]

    //  Slices are referential data , In this way, intercepting data is essentially the change of data pointer to memory address 
    //  So other slices on this memory address change , The original slice data will change , as follows :
    arr1[1] = 88
    fmt.Println(arr) //[1 2 3 88 5 6 20 30]
    
------------------------- Deep copy ----------------------------
    //  If the slice was meant to refer to another slice , But when the element increases more than the slice capacity , It will open up new space , It doesn't affect the original data ( The pointer to the data is different )
    var arr2 = []int{1, 2, 3}
    fmt.Println(arr2)            //[1 2 3]
    fmt.Println(sliceAddr(arr2)) // Original slice data pointer  824633771136
    arr3 := arr2
    fmt.Println(arr3)            //[1 2 3]
    fmt.Println(sliceAddr(arr3)) // New slice data pointer  824633771136
    arr3 = append(arr3, 4, 5, 6) // New element exceeds capacity , Re open up space 
    fmt.Println(arr2)            //[1 2 3]
    fmt.Println(arr3)            //[1 2 3 4 5 6]
    fmt.Println(sliceAddr(arr2)) // Original slice data pointer  824633771136
    fmt.Println(sliceAddr(arr3)) // After exceeding the capacity , New space slice data pointer  824633787232

    //  Use built-in functions copy(), Copy a copy of the data , The modified data also has no effect on the original data 
    // copy() And slice expansion , It's a whole copy of the original data slice , There is no reference to the memory address of the original data , This is called deep copy , The modification has no effect on the original data 
    var arr4 = make([]int, len(arr))
    copy(arr4, arr)
    arr4[1] = 100
    fmt.Println(arr)  //[1 2 3 88 5 6 20 30]
    fmt.Println(arr4) //[1 100 3 88 5 6 20 30]

}

func sliceAddr(s []int) uintptr {
    return (*reflect.SliceHeader)(unsafe.Pointer(&s)).Data
}

Traverse

package main

import (
    "fmt"
)

func main() {
    var arr = []int{1, 2, 3, 4, 5, 6, 20, 30}
    for range arr {
        fmt.Printf("%s ", "a") // a a a a a a a a 
    }
    fmt.Println("")
    for k := range arr {
        fmt.Printf("%d ", arr[k]) //1 2 3 4 5 6 20 30 
    }
    fmt.Println("")

    for k, v := range arr {
        fmt.Printf("%d=>%d ", k, v) //0=>1 1=>2 2=>3 3=>4 4=>5 5=>6 6=>20 7=>30 
    }
    fmt.Println("")
    for i := 0; i < len(arr); i++ {
        fmt.Printf("%d ", arr[i]) //1 2 3 4 5 6 20 30
    }

}

版权声明
本文为[A night of smoke and clouds]所创,转载请带上原文链接,感谢