golang 数组基础用法


数组

定义

数组是用来存储相同唯一类型的,一组已编号且长度固定的序列

声明 && 初始化数组

数组声明需要指定元素类型及元素个数,语法格式如下:

var arr_name [num] arr_type
var name [5]string{"xiaoming","xiaofang"}
  • 声明长度为5 类型为string的数组 var arr1 [5]string

  • 声明并初始化,不写长度用…代替

  • 声明并初始化,写长度

package main

import "fmt"

func main() {

	var arr1 [5]string
	fmt.Println(arr1)
	arr1[1] = "golang"
	fmt.Println(arr1)

	var arr2 = [...]int{1, 2, 3}
	fmt.Println(arr2)
}

访问数组元素

package main

import "fmt"

func main() {
	var arr1 [10]int
	// 根据索引赋值
	for i := 0; i < 10; i++ {
		arr1[i] = i
	}
	// 根据索引查询数据
	for i := 0; i < 10; i++ {
		fmt.Println(arr1[i])
	}
}
  • 索引越界 编译检查报错 invalid array index 20 (out of bounds for 10-element array)
  • fmt.Println(arr1[20])

指针数组

  • 数组的元素除了是某个类型外,还可以是某个类型的指针
  • new函数返回一个TYPE 类型的数据结构划分内存并执行默认的初始化操作,然后返回这个数据对象的指针
package main

import "fmt"

func main() {

	var arr1 [5]*int
	// 根据索引赋值
	arr1[0] = new(int)
	arr1[1] = new(int)
	arr1[2] = new(int)
	arr1[3] = new(int)
	arr1[4] = new(int)
	fmt.Println(arr1)
	*arr1[0] = 10
	*arr1[1] = 2
	fmt.Println(arr1)
	for i := 0; i < len(arr1); i++ {
		fmt.Printf("[索引:%d 值是: %d]\n", i, *arr1[i])
	}
	/*
	   [0xc00001a098 0xc00001a0b0 0xc00001a0b8 0xc00001a0c0 0xc00001a0c8]
	   [0xc00001a098 0xc00001a0b0 0xc00001a0b8 0xc00001a0c0 0xc00001a0c8]
	   [索引:0 值是: 10]
	   [索引:1 值是: 2]
	   [索引:2 值是: 0]
	   [索引:3 值是: 0]
	   [索引:4 值是: 0]
	*/

}
  • 只声明不初始化 ,必须用new,空值 panic: runtime error: invalid memory address or nil pointer dereference

  • 普通数据深拷贝的例子

    • 判定依据就是 新老对象的指针%p &var是一致的,说明是浅拷贝,否则是深拷贝
package main

import "fmt"

func main() {

	arr1 := [2]int{1, 2}
	var arr2 [2]int
	arr2 = arr1
	fmt.Printf("[%v %p]\n", arr1, &arr1)
	fmt.Printf("[%v %p]\n", arr2, &arr2)
	arr2[1] = 20
	fmt.Printf("[%v %p]\n", arr1, &arr1)
	fmt.Printf("[%v %p]\n", arr2, &arr2)

}
  • 两个数组指针直接复制
    • 原因是内部存放的是指针,指向同一块地址,直接赋值,内容都一样,看起来是浅拷贝
    • 但是其实数据copy是深拷贝
package main

import "fmt"

func main() {

	var arr1 [3]*string
	arr2 := [3]*string{new(string), new(string), new(string)}

	*arr2[0] = "k1"
	*arr2[1] = "k2"
	*arr2[2] = "k3"
	arr1 = arr2
	fmt.Println(arr1)
	fmt.Println(arr2)
	for i := 0; i < 3; i++ {
		fmt.Printf("[arr1 :%d :%v %v]\n", i, *arr1[i], arr1[i])
		fmt.Printf("[arr2 :%d :%v %v]\n", i, *arr2[i], arr2[i])
	}
	fmt.Printf("[%v %p]\n", arr1, &arr1)
	fmt.Printf("[%v %p]\n", arr2, &arr2)
	/*
	    [0xc00004c240 0xc00004c250 0xc00004c260]
	   [0xc00004c240 0xc00004c250 0xc00004c260]
	   [arr1 :0 :k1 0xc00004c240]
	   [arr2 :0 :k1 0xc00004c240]
	   [arr1 :1 :k2 0xc00004c250]
	   [arr2 :1 :k2 0xc00004c250]
	   [arr1 :2 :k3 0xc00004c260]
	   [arr2 :2 :k3 0xc00004c260]
	*/
}

数组的特点

  • 固定长度:这意味着数组不可增长、不可缩减。想要扩展数组,只能创建新数组,将原数组的元素复制到新数组。
  • 内存连续:这意味可以在缓存中保留的时间更长,搜索速度更快,是一种非常高效的数据结构,同时还意味着可以通过数值的方式(arr[index])索引数组中的元素。
  • 固定类型:固定类型意味着限制了每个数组元素可以存放什么样的数据,以及每个元素可以存放多少字节的数据。

数组是值类型,就是深拷贝

  • 举例
package main

import "fmt"

func main() {

   arr1 := [2]int{1, 2}
   var arr2 [2]int
   arr2 = arr1
   fmt.Printf("[%v %p]\n", arr1, &arr1)
   fmt.Printf("[%v %p]\n", arr2, &arr2)
   arr2[1] = 20
   fmt.Printf("[%v %p]\n", arr1, &arr1)
   fmt.Printf("[%v %p]\n", arr2, &arr2)

}

所有的值类型变量在赋值和作为参数传递时都将产生一次复制

把数组传递给函数 数组指针

  • 数组是一个值类型,所有的值类型变量在赋值和作为参数传递时都将产生一次复制操作
  • 从内存和性能上来看,在函数间传递数组是一个开销很大的操作。因为无论这个数组有多长,都会完整复制,并传递给函数
  • 数组指针只需要很小传递
package main

import (
	"fmt"
	"unsafe"
)

func bigArrPoint(arr *[1e6]int64) {
	fmt.Printf("[数组指针复制:大小:%d字节]\n", unsafe.Sizeof(arr))
}

func bigArr(arr [1e6]int64) {
	fmt.Printf("[数组复制:大小:%d字节]\n", unsafe.Sizeof(arr))
}

func main() {

	var arr [1e6]int64
	bigArr(arr)
	bigArrPoint(&arr)
}

多维数组

  • 多维数组的典型用例是平面坐标(二维数组)和三维坐标(三维数组)
  • Golang 的数组本身只有一个维度,但是我们可以组合多个数组从而创建出多维数组
package main

import (
	"fmt"
)

func main() {

	arr1 := [4][2]int{{10, 11}, {20, 21}, {30, 31}, {40, 41}}
	fmt.Println(arr1)
	fmt.Println(arr1[3][1])
}

Author: qianli
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint policy. If reproduced, please indicate source qianli !
  TOC