Go语言中的数组和切片 len cap append copy

简介: Go语言中的数组和切片 len cap append copy

aHR0cHM6Ly9jb2RpbmczbWluLm9zcy1hY2NlbGVyYXRlLmFsaXl1bmNzLmNvbS9jb2RpbmczbWluLzIwMjAtMDUtMDUtMTUzODMyLmpwZw.png

先来看看Golang中的数组


其实在循环那一节用到过数组,我快速介绍一下。

  • 数组中是固定长度的连续空间(内存区域)
  • 数组中所有元素的类型是一样的


  var a1 [10]int
  //初始化数组
  var b1 = [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}

多维数组


//声明二维数组,只要 任意加中括号,可以声明更多维,相应占用空间指数上指
  var arr [3][3]int
  //赋值
  arr = [3][3]int{
    {1, 2, 3},
    {2, 3, 4},
    {3, 4, 5},
  }


何谓切片?


类比c语言,一个int型数组int a[10],a的类型是int*,也就是整型指针,而c语言中可以使用malloc()动态的分配一段内存区域,c++中可以用new()函数。例如:

int* a = (int *)malloc(10);
int* b = new int(4);

此时,ab的类型也是int*ab此时分配内存的方式类似于go语言中的切片。

Go的数组和切片都是从c语言中延续过来的设计。


有何不同?


var sliceTmp []int


可以看到和c不同的是,go可以声明一个空切片(默认值为nil),然后再增加值的过程中动态的改变切片值大小。


怎么动态增加?


增加的方式只有一种,使用append函数追加。

sliceTmp = append(sliceTmp, 4)
sliceTmp = append(sliceTmp, 5)

每个切片有长度len和容量cap两个概念,长度是我们最熟知的,和数组长度相同,可以直接用来遍历。


for _,v := range slice1{
    fmt.Println(v)
  }


用切糕来对比


aHR0cHM6Ly9jb2RpbmczbWluLm9zcy1hY2NlbGVyYXRlLmFsaXl1bmNzLmNvbS9jb2RpbmczbWluLzIwMjAtMDUtMDUtMDgwNjE3LmpwZw.png


每个切片,在声明或扩建时会分配一段连续的空间,称为容量cap,是不可见的;真正在使用的只有一部分连续的空间,称为长度len,是可见的。


每次append时,如果发现cap已经不足以给len使用,就会重新分配原cap两倍的容量,把原切片里已有内容全部迁移过去。


新分配的空间也是连续的,不过不一定直接在原切片内存地址处扩容,也有可能是新的内存地址。


切片的长度与容量,len cap append copy

slice1 := []int{1, 2, 3}


普通切片的声明方式,长度和容量是一致的。

len=3 cap=3 slice=[1 2 3]


当然,控制权在我们手上,我们可以自己控制长度和容量,

slice1 = make([]int, 3, 5) // 3 是长度 5 是容量


输出

len=3 cap=5 slice=[0 0 0]


尝试使用一般的方式扩容

slice1[len(slice1)] = 4 
//报错 panic: runtime error:
//index out of range [3] with length 3


这种方式是会报错的,虽然容量是 5 ,但是数组长度是3,这里是以长度为准,而不是容量,append内部如果超过容量相当于创建了一个新数组,每个新数组都是定长的,只不过外部是切片。

尝试扩容

slice1 = append(slice1, 4)


输出,可以发现len扩容了!

len=4 cap=5 slice=[0 0 0 4]


让我们连续扩容,让容量超过5

slice1 = append(slice1, 5)
slice1 = append(slice1, 6) // 到这里长度超过了容量,容量自动翻倍为 5*2


输出

len=6 cap=10 slice=[0 0 0 4 5 6]


上面的过程,我 用自己的代码模拟一遍

// 上面容量自动翻倍的过程可以看作和下面一致
  slice1 = make([]int, 3, 5) // 3 是长度 5 是容量
  slice1 = append(slice1, 4)
  slice1 = append(slice1, 5)
// 长度不变,容量自动翻倍为 5*2
  slice2 := make([]int, len(slice1), 
          (cap(slice1))*2)
  // 拷贝 slice1 的内容到 slice2 
  // 注意是后面的拷贝给前面
  copy(slice2, slice1) 
  slice2 = append(slice2, 6) 

所以,你理解容量,长度的概念了吗?


aHR0cHM6Ly9jb2RpbmczbWluLm9zcy1hY2NlbGVyYXRlLmFsaXl1bmNzLmNvbS9jb2RpbmczbWluLzIwMjAtMDQtMjgtOTZjZDBlZDNiMzAyNDAzNjk1MzE1ODMxYzYyOWQ3ZTMlMjAtNC0ucG5n.png

相关文章
|
1天前
|
存储 安全 编译器
go语言中进行不安全的类型操作
【5月更文挑战第10天】Go语言中的`unsafe`包提供了一种不安全但强大的方式来处理类型转换和底层内存操作。包含两个文档用途的类型和八个函数,本文也比较了不同变量和结构体的大小与对齐系数,强调了字段顺序对内存分配的影响。
38 8
go语言中进行不安全的类型操作
|
1天前
|
Go
配置go语言下载包 - 蓝易云
这个命令会将包下载到你的GOPATH目录下,并自动安装它。
22 1
|
2天前
|
安全 Go 调度
Go语言中的并发编程
Go语言自带了强大的并发编程能力,它的协程机制可以让程序轻松地实现高并发。本文将从并发编程的基础概念出发,介绍Go语言中的协程机制、通道和锁等相关知识点,帮助读者更好地理解并发编程在Go语言中的实践应用。
|
4天前
|
Ubuntu Unix Linux
【GO基础】1. Go语言环境搭建
【GO基础】1. Go语言环境搭建
|
5天前
|
JSON 前端开发 Go
lucky - go 语言实现的快速开发平台
go 语言实现的快速开发平台,自动生成crud代码,前端页面通过json配置,无需编写前端代码。
11 0
|
6天前
|
存储 Java Go
Go 语言切片如何扩容?(全面解析原理和过程)
Go 语言切片如何扩容?(全面解析原理和过程)
16 2
|
6天前
|
负载均衡 Go 调度
使用Go语言构建高性能的Web服务器:协程与Channel的深度解析
在追求高性能Web服务的今天,Go语言以其强大的并发性能和简洁的语法赢得了开发者的青睐。本文将深入探讨Go语言在构建高性能Web服务器方面的应用,特别是协程(goroutine)和通道(channel)这两个核心概念。我们将通过示例代码,展示如何利用协程处理并发请求,并通过通道实现协程间的通信和同步,从而构建出高效、稳定的Web服务器。
|
6天前
|
算法 Go 分布式数据库
构建高可用的分布式数据库集群:使用Go语言与Raft共识算法
随着数据量的爆炸式增长,单一数据库服务器已难以满足高可用性和可扩展性的需求。在本文中,我们将探讨如何使用Go语言结合Raft共识算法来构建一个高可用的分布式数据库集群。我们不仅会介绍Raft算法的基本原理,还会详细阐述如何利用Go语言的并发特性和网络编程能力来实现这一目标。此外,我们还将分析构建过程中可能遇到的挑战和解决方案,为读者提供一个完整的实践指南。
|
6天前
|
消息中间件 Go API
基于Go语言的微服务架构实践
随着云计算和容器化技术的兴起,微服务架构成为了现代软件开发的主流趋势。Go语言,以其高效的性能、简洁的语法和强大的并发处理能力,成为了构建微服务应用的理想选择。本文将探讨基于Go语言的微服务架构实践,包括微服务的设计原则、服务间的通信机制、以及Go语言在微服务架构中的优势和应用案例。