首页 > 其他分享 >Go语言实战之数组的内部实现和基础功能

Go语言实战之数组的内部实现和基础功能

时间:2022-12-12 15:31:44浏览次数:32  
标签:实战 int 元素 整型 数组 Go array 声明


写在前面


  • 嗯,学习​​GO​​,所以有了这篇文章
  • 博文内容为​​《GO语言实战》​​读书笔记之一
  • 主要涉及数组相关知识

世上除了爹娘,再没有人是理所应当对你好的。 ——烽火戏诸侯《剑来》


数组的内部实现和基础功能

数组在​​GO​​​里是​​切片​​​和​​映射​​​的​​基础数据结构​​,学过其他语言的小伙伴对数组应该都不陌生。基本上所有的语言都会有数组的概念。

​数组(Array)​​​是一种​​线性​​​数据结构。它用一组​​连续​​​的​​内存空间​​​,来存储一组具有​​相同类型的数据​​。

内部实现

在​​Go​​​语言里,数组是一个长度固定的数据类型,用于存储一段具有相同的类型的元素的连续块。数组存储的类型可以是内置类型,如​​整型​​​或者​​字符串​​​,也可以是某种​​结构类型​​。

数组因为其占用的内存是连续分配的。CPU能把正在使用的数据缓存更久的时间。而且内存连续很容易计算索引,可以快速迭代数组里的所有元素。数组的类型信息可以提供每次访问一个元素时需要在内存中移动的距离。

关于上面这句话我个人理解:

  • 数组结构上​​内存连续分配​​​,所以很容易的​​计算索引​​​(元素的相对地址),​​等差数列​​​,同时​​内存连续​​​,可以很好的使用​​CUP的缓存​​​,当​​CPU​​​访问​​首地址​​​时,会自动的​​从内存中加载当前数组其他元素到CUP Cache​​​,加载多少由​​CPU Cache Line​​​决定,所以​​CPU​​​能把正在使用的数据缓存更久的时间。当内存不连续时,就无法读到 ​​CPU Cache​​​,只能重复的从内存读取数据元素,也就不能充分利用到了​​CPU Cache​​的特性
  • 通过索引可以快速的迭代数组元素,如果用a代表数组的首地址,​​a[0]​​​就是偏移量为​​0​​​的位置,也就是首地址,​​a[k]​​​就表示偏移量​​k​​​个​​type_size​​​的位置,所以计算​​a[k]​​​的内存地址只需要用这个公式​​a[k]_address = base_address + k * type_size​​,所以说可以通过索引确认内存地址快速迭代,理论上时间复制度为常量级。

声明和初始化

​声明数组​​​时需要指定内部存储的数据的​​类型​​​,以及需要存储的元素的​​数量​

  • 声明一个数组,并设置为零值
var arrays [5]int
  • 使用数组字面量声明数组
arrays := [5]int{10,12,13}
  • 让 ​​Go​​ 自动计算声明数组的长度
array := [...]int{10, 20,30, 40, 50}
  • 声明数组并指定特定元素的值,用具体值初始化索引为​​1​​​和​​2​​的元素
array := [5]int{1: 10, 2: 20}

使用数组

内存布局是连续的,所以数组是效率很高的数据结构,在访问数组里任意元素的时候,使用​​[]​​运算符

  • 访问数组元素
//声明一个包含 5 个元素的整型数组
array := [5]int{10, 20, 30, 40, 50}
// 修改索引为 2 的元素的值
array[2] = 35

​声明一个所有元素都是指针的数组​​​。使用​​*运算符​​就可以访问元素指针所指向的值

  • 访问指针数组的元素
// 声明包含 5 个元素的指向整数的数组
// 用整型指针初始化索引为 0 和 1 的数组元素
array := [5]*int{0: new(int), 1: new(int)}
// 为索引为 0 和 1 的元素赋值
*array[0] = 10
*array[1] = 20

在 ​​Go​​ 语言里,数组是一个值。这意味着数组可以用在赋值操作中。变量名代表整个数组,同样类型的数组可以赋值给另一个数组

// 声明第一个包含 5 个元素的字符串数组
var array1 [5]string
// 声明第二个包含 5 个元素的字符串数组
// 用颜色初始化数组
array2 := [5]string{"Red", "Blue", "Green", "Yellow", "Pink"}
// 把 array2 的值复制到 array1
array1 = array2
  • 编译器会阻止类型不同的数组互相赋值
package main

import "fmt"

func main() {
fmt.Println("你好,世界")
// 声明第一个包含 4 个元素的字符串数组
var array1 [4]string

// 声明第二个包含 5 个元素的字符串数组
// 使用颜色初始化数组
array2 := [5]string{"Red", "Blue", "Green", "Yellow", "Pink"}

// 将 array2 复制给 array1
array1 = array2
}

​go vet​​检查

┌──[[email protected]]-[/usr/local/go/src/demo]
└─$go fmt array.go
array.go
┌──[[email protected]]-[/usr/local/go/src/demo]
└─$vim array.go
┌──[[email protected]]-[/usr/local/go/src/demo]
└─$go vet array.go
# command-line-arguments
vet: ./array.go:15:11: cannot use array2 (variable of type [5]string) as [4]string value in assignment
┌──[[email protected]]-[/usr/local/go/src/demo]
└─$

多维数组

数组本身只有一个维度,不过可以组合多个数组创建多维数组。多维数组很容易管理具有父子关系的数据或者与坐标系相关联的数据

  • 声明二维数组
// 声明一个二维整型数组,两个维度分别存储 4 个元素和 2 个元素
var array [4][2]int
// 使用数组字面量来声明并初始化一个二维整型数组
array := [4][2]int{{10, 11}, {20, 21}, {30, 31}, {40, 41}}
// 声明并初始化外层数组中索引为 1 个和 3 的元素
array := [4][2]int{1: {20, 21}, 3: {40, 41}}
// 声明并初始化外层数组和内层数组的单个元素
array := [4][2]int{1: {0: 20}, 3: {1: 41}}
  • 访问二维数组的元素
// 声明一个 2×2 的二维整型数组
var array [2][2]int
// 设置每个元素的整型值
array[0][0] = 10

只要类型一致,就可以将多维数组互相赋值

// 声明两个不同的二维整型数组
var array1 [2][2]int
var array2 [2][2]int
// 为每个元素赋值
array2[0][0] = 10
array2[0][1] = 20
array2[1][0] = 30
array2[1][1] = 40
  • 同样类型的多维数组赋值
// 将 array2 的值复制给 array1
array1 = array2
  • 使用索引为多维数组赋值
// 将 array1 的索引为 1 的维度复制到一个同类型的新数组里
var array3 [2]int = array1[1]
// 将外层数组的索引为 1、内层数组的索引为 0 的整型值复制到新的整型变量里
var value int = array1[1][0]

在函数间传递数组

根据内存和性能来看,在函数间传递数组是一个开销很大的操作。​​在函数之间传递变量时,总是以值的方式传递的​​。如果这个变量是一个数组,意味着整个数组,不管有多长,都会完整复制,并传递给函数。

  • 使用值传递,在函数间传递大数组
// 声明一个需要 8 MB 的数组,创建一个包含 100 万个 int 类型元素的数组
var array [1e6]int
// 将数组传递给函数 foo
foo(array)
// 函数 foo 接受一个 100 万个整型值的数组
func foo(array [1e6]int) {
...
}

每次函数​​foo​​​被调用时,必须在栈上分配​​8 MB​​的内存

还有一种更好且更有效的方法来处理这个操作。可以只传入指向数组的指针,这样只需要复制​​8​​​字节的数据而不是​​8 MB​​ 的内存数据到栈上

  • 使用指针在函数间传递大数组
// 分配一个需要 8 MB 的数组
var array [1e6]int
// 将数组的地址传递给函数 foo
foo(&array)
// 函数 foo 接受一个指向 100 万个整型值的数组的指针
func foo(array *[1e6]int) {
...
}

将数组的地址传入函数,只需要在栈上分配 8 字节的内存给指针就可以.


标签:实战,int,元素,整型,数组,Go,array,声明
From: https://blog.51cto.com/u_13474506/5929857

相关文章

  • MongoDB的local数据库
    每个mongod实例都有自己的local数据库,用于存储复制过程中使用的数据和其他特定于实例的数据。local数据库对复制不可见:不复制local数据库中的集合。local数据库中的集合:......
  • 更严格的代码格式化工具 gofumpt
    gofmt是golang自带的代码自动格式化工具,是保证Go代码风格一致的大杀器。我们这次要推荐的gofumpt在gofmt的基础上添加了一系列更加严格的格式化规则,并保证了对go......
  • 从零开始搭建Go语言开发环境
    从零开始搭建Go语言开发环境发布于2020/03/09,更新于2022/11/1917:42:52|Golang|总阅读量:2194次Go1.14版本,一步一步,从零搭建Go语言开发环境。因为Go语言及相关编辑......
  • 每日算法之把数组排成最小的数
    JZ45把数组排成最小的数描述输入一个非负整数数组numbers,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组[3,32,321],则打印出这三......
  • Go语言介绍
    为什么你应该学习Go语言?2022-12-1214:09:08为什么互联网世界需要Go语言世界上已经有太多太多的编程语言了,为什么又出来一个Go语言?硬件限制:摩尔定律已然失效摩尔定律:......
  • golang的chan用法与fatal error: all goroutines are asleep - deadlock!
    例子1:funcmain(){ch:=make(chanint)ch<-1fmt.Println("发送成功")}上面这段代码能够通过编译,但是执行的时候会出现以下错误:为什么会出现deadlock错误呢?......
  • Go/Python gRPC实践
    gRPC框架&ProtoBuf安装相关工具:pip3installgrpciopip3installgrpcio-toolsprotobuf3有自己专门的定义的格式,基于此可以生成不同的脚本编写示例的protobuf3:......
  • 数组名的注意事项
    intmain(){ intarr[]={1,2,3,4,5,6,7,8,9,0}; printf("%p\n",arr);//数组名是数组首个元素的地址 printf("%p\n",&arr[0]);//数组名是数组首个元素的地址 printf("......
  • 每日一抄 Go语言使用select切换协程
    看了两篇博客,一个说:在任何一个case中执行break或者return,select就结束了。另一个说:break只能跳出select中的一个case验证了一下,不知道对不对,感觉是跳出了整个selec......
  • Python爬虫实战,requests+openpyxl模块,爬取手机商品信息数据(附源码)
    前言今天给大家介绍的是Python爬取手机商品信息数据,在这里给需要的小伙伴们代码,并且给出一点小心得。首先是爬取之前应该尽可能伪装成浏览器而不被识别出来是爬虫,基本的......