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

相关文章
|
2月前
|
存储 安全 Java
【Golang】(4)Go里面的指针如何?函数与方法怎么不一样?带你了解Go不同于其他高级语言的语法
结构体可以存储一组不同类型的数据,是一种符合类型。Go抛弃了类与继承,同时也抛弃了构造方法,刻意弱化了面向对象的功能,Go并非是一个传统OOP的语言,但是Go依旧有着OOP的影子,通过结构体和方法也可以模拟出一个类。
208 1
|
4月前
|
Cloud Native 安全 Java
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
301 1
|
4月前
|
Cloud Native Go API
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
402 0
|
4月前
|
Cloud Native Java Go
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
261 0
|
4月前
|
Cloud Native Java 中间件
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
231 0
|
4月前
|
Cloud Native Java Go
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
336 0
|
4月前
|
数据采集 Go API
Go语言实战案例:多协程并发下载网页内容
本文是《Go语言100个实战案例 · 网络与并发篇》第6篇,讲解如何使用 Goroutine 和 Channel 实现多协程并发抓取网页内容,提升网络请求效率。通过实战掌握高并发编程技巧,构建爬虫、内容聚合器等工具,涵盖 WaitGroup、超时控制、错误处理等核心知识点。
|
4月前
|
数据采集 JSON Go
Go语言实战案例:实现HTTP客户端请求并解析响应
本文是 Go 网络与并发实战系列的第 2 篇,详细介绍如何使用 Go 构建 HTTP 客户端,涵盖请求发送、响应解析、错误处理、Header 与 Body 提取等流程,并通过实战代码演示如何并发请求多个 URL,适合希望掌握 Go 网络编程基础的开发者。
|
5月前
|
JSON 前端开发 Go
Go语言实战:创建一个简单的 HTTP 服务器
本篇是《Go语言101实战》系列之一,讲解如何使用Go构建基础HTTP服务器。涵盖Go语言并发优势、HTTP服务搭建、路由处理、日志记录及测试方法,助你掌握高性能Web服务开发核心技能。
|
5月前
|
Go
如何在Go语言的HTTP请求中设置使用代理服务器
当使用特定的代理时,在某些情况下可能需要认证信息,认证信息可以在代理URL中提供,格式通常是:
408 0

热门文章

最新文章