【go】数组与切片

发布于:2025-04-04 ⋅ 阅读:(23) ⋅ 点赞:(0)

数组Array

重点:

数组是值类型

数组的定义

var 数组名 [数组大小] 数据类型,例如var intArr [5] int,定义完数组后数组里的元素有默认值。、

数组的地址&intArr、&intArr[0]。

数组占据连续的内存。

int通常是4字节(32位),而在64位系统上,int通常是8字节(64位)。

注意点:

    1. 数组:是同一种数据类型的固定长度的序列。
    2. 数组定义:var a [len]int,比如:var a [5]int,数组长度必须是常量,且是类型的组成部分。一旦定义,长度不能变。
    3. 长度是数组类型的一部分,因此,var a[5] intvar a[10]int是不同的类型。
    4. 数组可以通过下标进行访问,下标是从0开始,最后一个元素下标是:len-1
    for i := 0; i < len(a); i++ {
    }
    for index, v := range a {
    }
    5. 访问越界,如果下标在数组合法范围之外,则触发访问越界,会panic
    6. 数组是值类型,赋值和传参会复制整个数组,而不是指针。因此改变副本的值,不会改变本身的值。
    7.支持 "==""!=" 操作符,因为内存总是被初始化过的。
    8.指针数组 [n]*T,数组指针 *[n]T。

一维数组定义:

   全局:
    var arr0 [5]int = [5]int{1, 2, 3}
    var arr1 = [5]int{1, 2, 3, 4, 5}
	// 通过初始化值确定数组长度。
    var arr2 = [...]int{1, 2, 3, 4, 5, 6}
    var str = [5]string{3: "hello world", 4: "tom"}
    局部:
    a := [3]int{1, 2}           // 未初始化元素值为 0。
    b := [...]int{1, 2, 3, 4}   // 通过初始化值确定数组长度。
    c := [5]int{2: 100, 4: 200} // 使用索引号初始化元素。
    d := [...]struct {			// 结构体数组,前段是结构定义,后段是初始化
        name string
        age  uint8
    }{
        {"user1", 10}, // 可省略元素类型。
        {"user2", 20}, // 别忘了最后一行的逗号。
    }
package main

import (
	"fmt"
)

var arr0 [5]int = [5]int{1, 2, 3}
var arr1 = [5]int{1, 2, 3, 4, 5}
var arr2 = [...]int{1, 2, 3, 4, 5, 6}
var str = [5]string{3: "hello world", 4: "tom"}

func main(){
    a := [3]int{1, 2}           // 未初始化元素值为 0。
    b := [...]int{1, 2, 3, 4}   // 通过初始化值确定数组长度。
    c := [5]int{2: 100, 4: 200} // 使用引号初始化元素。
    d := [...]struct {
        name string
        age  uint8
    }{
        {"user1", 10}, // 可省略元素类型。
        {"user2", 20},
				{"刘小琦",21}, // 别忘了最后一行的逗号。
    }
		fmt.Printf("arr2的长度为:%d,str的长度为:%d\n", len(arr2), len(str))
    fmt.Println(arr0, arr1, arr2, str)
		fmt.Printf("b的长度为:%d,d的长度为:%d\n", len(a), len(d))
		fmt.Println(a, b, c, d)
}

多维数组:

 全局
    var arr0 [5][3]int
    var arr1 [2][3]int = [...][3]int{{1, 2, 3}, {7, 8, 9}}
    局部:
    a := [2][3]int{{1, 2, 3}, {4, 5, 6}}
    b := [...][2]int{{1, 1}, {2, 2}, {3, 3}} // 第 2 纬度不能用 "..."。第1维度可以用。
package main

import(
	"fmt"
)
// 全局
    var arr0 [5][3]int // 定义了就有默认值0
    var arr1 [2][3]int = [...][3]int{{1, 2, 3}, {7, 8, 9}}
func main(){
	 
    // 局部:
    a := [2][3]int{{1, 2, 3}, {4, 5, 6}}  // 初始化时就赋值
    b := [...][2]int{{1, 1}, {2, 2}, {3, 3}} // 第 2 纬度不能用 "..."。

	  	// 打印数组
    fmt.Println("arr0:", arr0)
    fmt.Println("arr1:", arr1)
    fmt.Println("a:", a)
    fmt.Println("b:", b)
}

数组是值传递

package main

import (
    "fmt"
)

func test(x [2]int) {
    fmt.Printf("x: %p\n", &x)
    x[1] = 1000
}

func main() {
    a := [2]int{}
    fmt.Printf("a: %p\n", &a)

    test(a)
    fmt.Println(a)
}

内置函数 len 和 cap 都返回数组长度 (元素数量)。

内置函数 lencap 在处理数组、切片和映射(map)时有所不同。

  • len 函数用于获取数组、切片、映射、字符串等的长度。对于数组和切片,len 返回的是元素的数量。对于映射,len 返回的是键值对的数量。
  • cap 函数用于获取数组、切片的容量。容量指的是底层数组可以容纳的元素数量。对于数组,其容量和长度是相同的,都是数组定义时的大小。而对于切片,容量是从切片的起始位置到底层数组的结束位置之间的元素数量,这可能比切片的长度要大。

简单来说,对于数组,lencap 返回的值是相同的。而对于切片,len 返回的是切片中实际包含的元素数量,而 cap 返回的是切片可以增长到的最大元素数量,直到需要分配新的底层数组。

数组指针

使用这种方式进行数组拷贝和传参,是可以改变原数组的

package main

import "fmt"

func printArr(arr *[5]int) {
	fmt.Println(arr)
    arr[0] = 10
    for i, v := range arr {
        fmt.Println(i, v)
    }
}

func main() {
    var arr1 [5]int
    printArr(&arr1)
    fmt.Println(arr1)
    arr2 := [...]int{2, 4, 6, 8, 10}
    printArr(&arr2)
    fmt.Println(arr2)
}

输出:但是我们注意我们传进去的是数组的地址,传参时使用&取地址符

&[0 0 0 0 0]
0 10
1 0
2 0
3 0
4 0
[10 0 0 0 0]
&[2 4 6 8 10]
0 10
1 4
2 6
3 8
4 10
[10 4 6 8 10]

练习:

生成十个随机数,求和,求平均值

package main

import (
	"fmt"
	"math/rand"
)

func main() {
	var arr [10]int;
	num:=0
	for i := 0; i < 10; i++ {
	// 生成0到99的随机数
	arr[i]=rand.Intn(100)
	num+=arr[i]
	}
	fmt.Println(arr)
	fmt.Println("数组元素之和为:",num)
	fmt.Printf("数组元素平均值为:%.2f\n",float64(num)/10)
}
	

求两数之和是否存在

package main

import(
	"fmt"
)

func Test(a [5]int,target int) (index1 int,index2 int){
	for i := 0; i < len(a); i++ {
		if a[i]>target{
			return -1,-1
		}else{
			for j := i+1; j < len(a); j++ {
				if a[j] == target-a[i] {
					return i,j
				}
			}
		}
	}
	return -1,-1
}

func main() {
	 b := [5]int{1, 3, 5, 8, 7}
	 index1,index2 := Test(b,10)
	 fmt.Println(index1,index2)
}

切片Slice

底层

在Go语言中,切片(slice)是对数组的一个动态、灵活的视图。切片本身并不存储数据,它只是封装了对底层数组的引用。一个切片的主要组成部分包括:

  1. 指针(Pointer):指向底层数组中切片开始位置的指针。
  2. 长度(Length):切片中元素的数量。
  3. 容量(Capacity):从切片开始位置到底层数组末尾可以容纳的元素数量。

注意点

需要说明,slice 并不是数组或数组指针。它通过内部指针和相关属性引用数组片段,以实现变长方案。是引用数据类型。

    1. 切片:切片是数组的一个引用,因此切片是引用类型。但自身是结构体,值拷贝传递。
    2. 切片的长度可以改变,因此,切片是一个可变的数组。
    3. 切片遍历方式和数组一样,可以用len()求长度。表示可用元素数量,读写操作不能超过该限制。 
    4. cap可以求出slice最大扩张容量,不能超出数组限制。0 <= len(slice) <= len(array),其中array是slice引用的数组。
	5. 切片的定义:var 变量名 []类型,比如 var str []string  var arr []int(长度没有确定的数组)
    6. 如果 slice == nil,那么 lencap 结果都等于 0

切片的定义

package main

import "fmt"

func main() {
   //1.声明切片
   var s1 []int
   if s1 == nil {
      fmt.Println("是空")
   } else {
      fmt.Println("不是空")
   }
   // 2.:=
   s2 := []int{}
   // 3.make()
   var s3 []int = make([]int, 0)
   fmt.Println(s1, s2, s3)
   // 4.初始化赋值
   var s4 []int = make([]int, 0, 0)
   fmt.Println(s4)
   s5 := []int{1, 2, 3}
   fmt.Println(s5)
   // 5.从数组切片
   arr := [5]int{1, 2, 3, 4, 5}
   var s6 []int
   // 前包后不包,左闭右开
   s6 = arr[1:4]
   fmt.Println(s6)
}

输出结果:

是空
[] [] []
[]
[1 2 3]
[2 3 4]

关于var s4 []int = make([]int, 0, 0)解释:

在Go语言中,切片是一个动态数组,它由三个部分组成:指向底层数组的指针、长度和容量。在你提供的代码中,var s4 []int = make([]int, 0, 0) 这一行创建了一个切片 s4,其长度和容量都是0。

  • 长度为0意味着切片中没有元素。
  • 容量为0意味着底层数组没有空间来存储元素,因此无法直接向这个切片追加元素而不需要重新分配内存。

当你打印切片时,Go会输出切片的长度和底层数组的内容。由于 s4 的长度为0,所以打印出来的切片内容看起来是空的,即 []

需要注意的是,虽然 s4 的长度和容量都是0,但它并不是 nil。一个 nil 切片在Go中表示它没有指向任何底层数组,而这里的 s4 是一个已经分配了内存(尽管长度和容量为0)的切片。这就是为什么在前面的代码中,s1 == nil 的判断为 true,而这里的判断为 false

如果你想要一个 nil 切片,可以这样声明:

var s4 []int

这样声明的切片 s4 将会是 nil,打印出来的结果也会是 [],但是它的内部状态与使用 make([]int, 0, 0) 创建的切片不同。

关于var s3 []int = make([]int, 0)解释:

这段代码 var s3 []int = make([]int, 0) 的意思是使用 Go 语言中的 make 函数来创建一个长度为 0 的整数切片 s3

具体来说:

  • make([]int, 0) 创建了一个切片,其类型为 []int(整数切片)。
  • 切片的长度(len)为 0,这意味着切片中不包含任何元素。
  • 由于没有指定容量(cap),切片的容量也会默认为 0。

因此,当你打印 s3 时,它会显示长度为 0,容量也为 0,内容为空。这与 s4 类似,只不过 s4 明确地指定了容量为 0。

切片初始化

左闭右开2:8,即[2,8)即2,3,4,5,6,7

var slice3 []int = arr[0:len(arr)] //var slice []int = arr[:],可以读到全部

package main

import (
    "fmt"
)

var arr = [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
var slice0 []int = arr[2:8]
var slice1 []int = arr[0:6]        //可以简写为 var slice []int = arr[:end]
var slice2 []int = arr[5:10]       //可以简写为 var slice[]int = arr[start:]
var slice3 []int = arr[0:len(arr)] //var slice []int = arr[:],可以读到全部
var slice4 = arr[:len(arr)-1]      //去掉切片的最后一个元素
func main() {
    fmt.Printf("全局变量:arr %v\n", arr)
    fmt.Printf("全局变量:slice0 %v\n", slice0)
    fmt.Printf("全局变量:slice1 %v\n", slice1)
    fmt.Printf("全局变量:slice2 %v\n", slice2)
    fmt.Printf("全局变量:slice3 %v\n", slice3)
    fmt.Printf("全局变量:slice4 %v\n", slice4)
    fmt.Printf("-----------------------------------\n")
    arr2 := [...]int{9, 8, 7, 6, 5, 4, 3, 2, 1, 0}
    slice5 := arr[2:8]
    slice6 := arr[0:6]         //可以简写为 slice := arr[:end]
    slice7 := arr[5:10]        //可以简写为 slice := arr[start:]
    slice8 := arr[0:len(arr)]  //slice := arr[:]
    slice9 := arr[:len(arr)-1] //去掉切片的最后一个元素
    fmt.Printf("局部变量: arr2 %v\n", arr2)
    fmt.Printf("局部变量: slice5 %v\n", slice5)
    fmt.Printf("局部变量: slice6 %v\n", slice6)
    fmt.Printf("局部变量: slice7 %v\n", slice7)
    fmt.Printf("局部变量: slice8 %v\n", slice8)
    fmt.Printf("局部变量: slice9 %v\n", slice9)
    fmt.Printf("局部变量: slice10 %v\n", slice10)
}

输出结果:

全局变量:arr [0 1 2 3 4 5 6 7 8 9]
全局变量:slice0 [2 3 4 5 6 7]
全局变量:slice1 [0 1 2 3 4 5]
全局变量:slice2 [5 6 7 8 9]
全局变量:slice3 [0 1 2 3 4 5 6 7 8 9]
全局变量:slice4 [0 1 2 3 4 5 6 7 8]
-----------------------------------
局部变量: arr2 [9 8 7 6 5 4 3 2 1 0]
局部变量: slice5 [2 3 4 5 6 7]
局部变量: slice6 [0 1 2 3 4 5]
局部变量: slice7 [5 6 7 8 9]
局部变量: slice8 [0 1 2 3 4 5 6 7 8 9]
局部变量: slice9 [0 1 2 3 4 5 6 7 8]
局部变量: slice10 [0 1 2 3 4 5 6 7 8 9]

网站公告

今日签到

点亮在社区的每一天
去签到