数组和切片

数组

在上文对基本类型做了介绍,如,现在需要10个int类型的变量,安装上文中介绍,我们可以声明10个变量,那么100个呢,这样是不有些麻烦?这时数组就出现了。可以声明一个长度为10的数组。数组是有长度和类型的集合,具有连续的存储空间

数组的声明

var aa [10]int // 标准声明
  var bb [10]int = [10]int{} //声明并初始化
  var cc = [10]int{1: 3, 5: 6} // 带索引的式初始化
  var mm = [...]int{1, 3, 4, 5} // 类型推断,长度推断
  dd := [10]int{1, 2} //类型推断式声明
  ee := [...]int{1, 2} 

数组的长度

获取数组的长度可以使用内置函数len取得, 声明数组时,数组的长度必须是一个常量,因为在编译完成后,就需要确定在内存中分配多大的空间进行存储。

数组的访问数组是一种高效的数据结构,它之前高效,是因为,数组在内存空间中的存储是连续,知道第一个元素的地址,通过公式可以快速的定位其要访问的存储单元的地址。公式为:索引对应内存地址=首地址+存储单元长度*索引值

通过查看内存的方式验证下这个结论

基于这种的内存结构,使得它的访问效率极高。在语言层面,数组的访问和赋值可以使用 aa[index] // 访问index的数组 aa[index] = 123 // 数组赋值

数组遍历使用go语言提供的for循环进行遍历

aa := [3]int{1,2,3}
for idx, val := range aa {
   fmt.Printf("下标:%d 值:%d\n",idx, val)
}

for i :=0; i< len(aa); i++ {
    fmt.Printf("下标:%d 值:%d\n",i, aa[i])
}

对数组的介绍在此先画个句号。数组是定长的。当需要使用变长的数组,怎么办?

  • 通过指针修改数组的值 上文中使用gdb工具,通过内存地址偏移的方式,查看了数组对应的内存的值,在程序中通过指 针的方式修改数组的值,go语言不能对不同类型的指针进行转化,但提供了一个强大的类型unsafe.Pointer可以基于它来实现,它有四个规则
  1. 任何指针都可以转换为unsafe.Pointer
  2. unsafe.Pointer可以转换为任何指针
  3. uintptr可以转换为unsafe.Pointer
  4. unsafe.Pointer可以转换为uintptr 基于这样的规则,可以实现以下代码,使用unsafe.Pointer和uintptr对指针进行运算
func main() {
  var aa = [3]int{}

  num1 := (*int)(unsafe.Pointer(&aa))
  *num1 = 1

  num2 := (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&aa)) + unsafe.Sizeof(int(0))*1))
  *num2 = 10

  num3 := (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&aa)) + unsafe.Sizeof(int(0))*2))
  *num3 = 4

  fmt.Println(aa)
}

切片

接着上文中提出的那个问题?怎么实现变长的数组?基于定长的数组,我们通过封装可以定义一个新的数据类型,实现不定长度的数组。这种数据类型被称为切片。

切片的声明切片的声明同数组声明一样,只是少了长度的指定 如:var aa []int 也可以通过类型推断的方式进行声明

初始化切片使用make进行初始化 aa := make([]int, 1,1) 通过字面量的方式初始化 aa := []int{1,2} 通过数组初始化 arr :=[5]int{1,2,3} aa := aa[0:3]

切片的追加切片内部是基于数组存储数据的,但存储的长度等于数组的最大长度时,再添加元素就会触发扩容,这个实现对外暴露出一个append的函数。如下:基于数组初始化的切片扩容 arr := [3]int{1,2,3} s1 := arr[0:2] // len(s1) < cap(s1) 不会发生扩容 s1 = append(s1, 1) // len(s1) = cap(s1) 触发扩容 s1 = append(s1, 1)

扩容机制:容量小于1024时,成倍的扩容,容量超过1024时,增长因子设为1.25,也就是说每次会增加25%的容量 看到很多书和资料都写的容量小于1024时,是二倍的扩容,但通过实验发现该描述并不准确,如:uint, int8切片在在首次扩容是8倍,uint16, int16是四倍的扩容

切片的迭代 切片的遍历和数组一样可以使用for range或for,此处不做展开

本文分享自微信公众号 - 云端漫记(BB_gzhsh),作者:暮雨

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-08-19

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 使用队列实现缓存淘汰

    在上文中实现了一个简单的缓存,并定时对缓存做过期处理。在这一篇文章中将通过基于队列的思想实现对缓存的限制

    暮雨
  • 使用vim打造go语言IDE

    在网上看到了一篇不错的关于vim的文章,参考网上给出的资料,花了些时间,将自己的vim打造成一块go语言的IDE。中间因为网路的问题,踩了很多的坑,就在此简单的...

    暮雨
  • 1108. IP 地址无效化

    给你一个有效的 IPv4 地址 address,返回这个 IP 地址的无效化版本。

    暮雨
  • CSS进阶10-分层显示

    (注1:如果有问题欢迎留言探讨,一起学习!转载请注明出处,喜欢可以点个赞哦!) (注2:更多内容请查看我的目录。)

    love丁酥酥
  • 删除元素

    一份执着✘
  • Angular ng-container使用的一个例子

    上图红色区域里的内容实际上是两个ng-container里定义的template被展开后得到的内容,但是ng-container本身没有出现在html里:

    Jerry Wang
  • 前端开发利器之静态服务器

    在进行前端页面开发时,为了调试方便,需要在本地启动一个静态文件服务器,而不需要与后端api服务一起部署。

    2Simple
  • Android LayoutTransiton实现简单的录制按钮

    最近公司要做的项目中要求实现一个简单的视频录制功能的组件,我简单设计了一个,主要功能就是开始,暂停,停止和显示录制时间长度。首先看一下效果图:

    砸漏
  • MySQL分库分表方案

    当一张表的数据达到几千万时,你查询一次所花的时间会变多,如果有联合查询的话,我想有可能会死在那儿了。分表的目的就在于此,减小数据库的负担,缩短查询时间。

    九州暮云
  • 偷偷迭代的重磅功能---小程序的像素处理能力

    不知道是什么时候?也许是春节期间?小程序的绘图api默默新增了两个接口: wx.canvasGetImageData,返回一个数组,用来描述 canvas 区域...

    花叔

扫码关注云+社区

领取腾讯云代金券