Go语言学习记录4——数组切片和变参函数

Posted 康娜喵

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Go语言学习记录4——数组切片和变参函数相关的知识,希望对你有一定的参考价值。

一.数组

1.1 数组的声明

1.1.1 常规声明

var a [3]Type
例如:

package main

import (  
    "fmt"
)

func main()   
    var a [3]int
    fmt.Println(a)

/* outputs:
[0 0 0]
*/

数组默认为了[0 0 0]

1.1.2 短变量形式的初始化

刚才那样的初始化可以用该语句代替:
a := [3]int0, 0, 0当然你可以填写需要的值
若后面的常量并没有包含全部的Size,那么还是会由0代替:a := [3]int12则生成的是[12 0 0]
若初始化数组的常量是简单的,那么可以用...让编译器去推导sizea := [3]int0, 0, 0,此时a还是一个长度为3的数组。

1.2 数组是一种特殊的类型

目前所用用到的数组都是采用的一个实例化的变量,而并非引用,如同这样:

package main

import "fmt"

func main()   
    a := [...]int1, 2, 3, 4
    b := a
    b[0] = 2
    fmt.Println("a is ", a)
    fmt.Println("b is ", b) 

/* outputs:
a is  [1 2 3 4]
b is  [2 2 3 4]
*/

1.3 数组的遍历

1.3.1 常规方法

一般所有数组都通用len获取数组长度的方法,通过遍历下标去遍历数组:

package main

import "fmt"

func main()   
    a := [...]int4, 3, 2, 1
    for i := 0; i < len(a); i++ 
        fmt.Printf("No.%d %d\\n", i, a[i])
    

1.3.2 range方法

通过这种类似于迭代器的方式去遍历数组,也可以得到和上例相同的结果:

package main

import "fmt"

func main()   
    a := [...]int4, 3, 2, 1
    for index, value := range a
        fmt.Printf("No.%d %d\\n", index, value)
    

如果你只需要indexvalue中的一个,那么用空标识符_代替不需要的那个元素:
例如,我只需要值:for _, value := range a 或者 for index := range a ,且这个indexvalue均为拷贝

1.4 二维数组

二维数组类似于其他语言,那么看这个example即可:

package main

import (  
    "fmt"
)

func printarray(a [3][2]string)   
    for _, v1 := range a 
        for _, v2 := range v1 
            fmt.Printf("%s ", v2)
        
        fmt.Printf("\\n")
    


func main()   
    a := [3][2]string
        "lion", "tiger",
        "cat", "dog",
        "pigeon", "peacock", //this comma is necessary. The compiler will complain if you omit this comma
    
    printarray(a)
    var b [3][2]string
    b[0][0] = "apple"
    b[0][1] = "samsung"
    b[1][0] = "microsoft"
    b[1][1] = "google"
    b[2][0] = "AT&T"
    b[2][1] = "T-Mobile"
    fmt.Printf("\\n")
    printarray(b)

二.切片

这里的切片又不是Python或者·matlab那样的切片,Go语言的切片是对原数组的部分的引用!!!
引用!!!

2.1 创建切片

2.1.1 已知数组创建切片

切片是左闭右开,实际引用的范围应该是[start, end - 1]

package main

import (  
    "fmt"
)

func main()   
    a := [5]int76, 77, 78, 79, 80
    var b []int = a[1:4]
    fmt.Println(b)

/* outputs:
[77 78 79]
*/

2.1.2 隐形创建一个数组并返回其切片

当我们省略掉初始化数组的长度时:

veggies := []string"potatoes","tomatoes","brinjal"

编译器会给我们创建一个数组,并返回其切片

2.2 修改切片

因为是引用,所以对切片的修改会作用到原数组:

package main

import (  
    "fmt"
)

func main()   
    a := [5]int76, 77, 78, 79, 80
	var b []int = a[1:4]
	fmt.Println("A_before is ", a)
	b[1]++
    fmt.Println("A_after  is ", a)

/* outputs
A_before is  [76 77 78 79 80]
A_after  is  [76 77 79 79 80]
*/

2.3 切片的长度与容量

package main

import (  
    "fmt"
)

func main()   
    a := [5]int76, 77, 78, 79, 80
	var b []int = a[1:3]
	fmt.Printf("length of slice %d capacity %d\\n", len(b), cap(b))

/* outputs
length of slice 2 capacity 4
*/

然后我们把var b []int = a[1:3]改为var b []int = a[1:4],此时的输出则是:length of slice 3 capacity 4
继续我们把var b []int = a[1:4]改为var b []int = a[2:4],此时的输出则是:length of slice 2 capacity 3
我们可以看到:

  • 切片的长度是由切片引用的长度决定的
  • 切片的容量是原数组的len减去切片起始的下标,代表切片的最大长度,这样就比较好理解了。

2.4 修改切片长度

通过再次对切片赋值,我们可以实现切片的长度修改,但是永远不能访问到第一次创建切片之外的元素

package main

import (  
    "fmt"
)

func main()   
    a := [5]int76, 77, 78, 79, 80
	var b []int = a[1:3]
	fmt.Println("B is ", b)
	b = b[0:1]
	fmt.Println("B is ", b)
	b = b[0:4]
	fmt.Println("B is ", b)

/* outputs
B is  [77 78]
B is  [77]
B is  [77 78 79 80]
*/

例如这个例子,我们可以访问到77(包括77)后面的所有元素,但是并不能访问到76

2.5 使用make创建切片

func make([]T, len, cap) []T可以用来创建切片,该函数接受长度和容量作为参数,返回切片。容量是可选的,默认与长度相同。使用 make 函数将会创建一个数组并返回它的切片。

package main

import (  
    "fmt"
)

func main()   
    i := make([]int, 5, 5)
    fmt.Println(i)

2.6 追加元素到切片

2.6.1 追加一个元素

使用append追加元素到切片,其用法为:append(s []T, x ...T) []T
先看这段代码:

package main

import (  
    "fmt"
)

func main()   
	a := [5]int76, 77, 78, 79, 80
	var b []int= a[:]
	b[0] = 1 
	fmt.Println("A is ", a)
	fmt.Println("B is ", b, "cap is", cap(b))
	b = append(b, 81)
	b[0] = 0 
	fmt.Println("B is ", b, "cap is", cap(b))
	fmt.Println("A is ", a)

/* outputs
A is  [1 77 78 79 80]
B is  [1 77 78 79 80] cap is 5
B is  [0 77 78 79 80 81] cap is 10
A is  [1 77 78 79 80]
*/

好奇怪,为什么此时的B又不是A的引用了?
因为:
append超出切片的容量时,append 将会在其内部创建新的数组,该数组的大小是原切片容量的 2 倍。最后 append 返回这个数组的全切片,即从 0 到 length - 1 的切片)
真是好奇怪的特性,对其他语言的使用者非常不友好

2.6.2 追加一个切片

如果我们要合并两个切片,我们可以使用...且鉴于go的特性,我们就不创建数组了:

package main

import (  
    "fmt"
)

func main()   
    veggies := []string"potatoes","tomatoes","brinjal"
    fruits := []string"oranges","apples"
    food := append(veggies, fruits...)
    fmt.Println("food:",food)

/* outputs
food: [potatoes tomatoes brinjal oranges apples]
*/

2.7 切片作为参数

切片的结构可以看做:

type slice struct   
    Length        int
    Capacity      int
    ZerothElement *byte

可以看到切片包含长度、容量、以及一个指向首元素的指针。所以我们可以通过这种方式传递实参

package main

import (  
    "fmt"
)
func add (vals []int) 
	for index := range vals 
		vals[index]++
	

func main()   
	a := [5]int76, 77, 78, 79, 80
	var b []int= a[:]
	fmt.Println("A is ", a)
	add(b)
	fmt.Println("A is ", a)

/* outputs
A is  [76 77 78 79 80]
A is  [77 78 79 80 81]
*/

2.8 多维切片

还是用一个例子表示:

package main

import (  
    "fmt"
)


func main()   
     pls := [][]string 
            "C", "C++",
            "javascript",
            "Go", "Rust",
            
    for _, v1 := range pls 
        for _, v2 := range v1 
            fmt.Printf("%s ", v2)
        
        fmt.Printf("\\n")
    

/* outputs
C C++  
JavaScript  
Go Rust  
*/

2.9 内存优化

因为数组只是引用,不管你怎么引用,数组一直都在内存里。如果我们只需要这个数组的一部分,那么我们可以使用copy(dst, src []T) int来拷贝一个数组,然后触发回收机制,把原来那个长数组回收。

package main

import (  
    "fmt"
)
func add (vals []int) 
	for index := range vals 
		vals[index]++
	

func main()   
	a := [5]int76, 77, 78, 79, 80
	var b []int= a[1:3]
	c := make([]int, len(b))
	copy(c, b)
	c[1] = 2
	fmt.Println("C is ", c)
	fmt.Println("A is ", a)

/* outputs
C is  [77 2]
A is  [76 77 78 79 80]
*/

理论上,如果不输出,此时的a所使用的内存已经会被回收。

三.可变参数

变参函数是指可以接收可变数量的参数的函数。
如果一个函数的最后一个参数由 ...T 表示,则表示该函数可以接收任意数量的类型为 T 的参数。
例如:

package main

import (  
    "fmt"
)

func find(num int, nums ...int)   
    fmt.Printf("type of nums is %T\\n", nums)
    found := false
    for i, v := range nums 
        if v == num 
            fmt.Println(num, "found at index", i, "in", nums)
            found = true
        
    
    if !found 
        fmt.Println(num, "not found in ", nums)
    
    fmt.Printf("\\n")

func main()   
    find(89, 89, 90, 95)
    find(45, 56, 67, 45, 90, 109)
    find(78, 38, 56, 98)
    find(87)

/* outputs
type of nums is []int
89 found at index 0 in [89 90 95]

type of nums is []int
45 found at index 2 in [56 67 45 90 109]

type of nums is []int
78 not found in  [38 56 98]

type of nums is []int
87 not found in  []
*/

以上是关于Go语言学习记录4——数组切片和变参函数的主要内容,如果未能解决你的问题,请参考以下文章

Go Example--变参函数

Go语言 可变参数(变参函数)

014_go语言中的变参函数

Go语言:变参函数

Go语言 | 03 数组指针切片用法

Go语言 | 03 数组指针切片用法