zoukankan      html  css  js  c++  java
  • go语言笔记——切片底层本质是共享数组内存!!!绝对不要用指针指向 slice切片本身已经是一个引用类型就是指针

    切片

    切片(slice)是对数组一个连续片段的引用(该数组我们称之为相关数组,通常是匿名的),所以切片是一个引用类型(因此更类似于 C/C++ 中的数组类型,或者 Python 中的 list 类型)

    切片是一个 长度可变的数组。

    多个切片如果表示同一个数组的片段,它们可以共享数据;因此一个切片和相关数组的其他切片是共享存储的,相反,不同的数组总是代表不同的存储。数组实际上是切片的构建块。

    优点 因为切片是引用,所以它们不需要使用额外的内存并且比使用数组更有效率,所以在 Go 代码中 切片比数组更常用。

    声明切片的格式是: var identifier []type(不需要说明长度)。

    一个切片在未初始化之前默认为 nil,长度为 0。

    切片也可以用类似数组的方式初始化:var x = []int{2, 3, 5, 7, 11}。这样就创建了一个长度为 5 的数组并且创建了一个相关切片。

    切片在内存中的组织方式实际上是一个有 3 个域的结构体:指向相关数组的指针,切片 长度以及切片容量。下图给出了一个长度为 2,容量为 4 的切片。——注意看其中的内存共享方式!!!

    • y[0] = 3 且 y[1] = 5
    • 切片 y[0:4] 由 元素 3, 5, 7 和 11 组成。

    注意 绝对不要用指针指向 slice。切片本身已经是一个引用类型,所以它本身就是一个指针!!

    7.2.2 将切片传递给函数

    如果你有一个函数需要对数组做操作,你可能总是需要把参数声明为切片。当你调用该函数时,把数组分片,创建为一个 切片引用并传递给该函数。这里有一个计算数组元素和的方法:

    func sum(a []int) int {
    	s := 0
    	for i := 0; i < len(a); i++ {
    		s += a[i]
    	}
    	return s
    }
    
    func main() {
    	var arr = [5]int{0, 1, 2, 3, 4}
    	sum(arr[:])
    }

    7.2.3 用 make() 创建一个切片

    当相关数组还没有定义时,我们可以使用 make() 函数来创建一个切片 同时创建好相关数组:var slice1 []type = make([]type, len)

    下图描述了使用 make 方法生成的切片的内存结构:

    7.2.4 new() 和 make() 的区别

    看起来二者没有什么区别,都在堆上分配内存,但是它们的行为不同,适用于不同的类型。

    • new(T) 为每个新的类型T分配一片内存,初始化为 0 并且返回类型为*T的内存地址:这种方法 返回一个指向类型为 T,值为 0 的地址的指针,它适用于值类型如数组和结构体(参见第 10 章);它相当于 &T{}
    • make(T) 返回一个类型为 T 的初始值,它只适用于3种内建的引用类型:切片、map 和 channel(参见第 8 章,第 13 章)。

    换言之,new 函数分配内存,make 函数初始化;下图给出了区别:

    在图 7.3 的第一幅图中:

    var p *[]int = new([]int) // *p == nil; with len and cap 0
    p := new([]int)

    在第二幅图中, p := make([]int, 0) ,切片 已经被初始化,但是指向一个空的数组。

    以上两种方式实用性都不高。下面的方法:

    var v []int = make([]int, 10, 50)

    或者

    v := make([]int, 10, 50)

    这样分配一个有 50 个 int 值的数组,并且创建了一个长度为 10,容量为 50 的 切片 v,该 切片 指向数组的前 10 个元素。

    转自:https://github.com/Unknwon/the-way-to-go_ZH_CN/blob/master/eBook/07.2.md

    7.6.3 字符串和切片的内存结构

    字符串 string s = "hello" 和子字符串 t = s[2:3] 在内存中的结构可以用下图表示:

    7.6.8 切片和垃圾回收

    切片的底层指向一个数组,该数组的实际容量可能要大于切片所定义的容量。只有在没有任何切片指向的时候,底层的数组内层才会被释放,这种特性有时会导致程序占用多余的内存。

    示例 函数 FindDigits 将一个文件加载到内存,然后搜索其中所有的数字并返回一个切片。

    var digitRegexp = regexp.MustCompile("[0-9]+")
    
    func FindDigits(filename string) []byte {
        b, _ := ioutil.ReadFile(filename)
        return digitRegexp.Find(b)
    }

    这段代码可以顺利运行,但返回的 []byte 指向的底层是整个文件的数据。只要该返回的切片不被释放,垃圾回收器就不能释放整个文件所占用的内存。换句话说,一点点有用的数据却占用了整个文件的内存。

    想要避免这个问题,可以通过拷贝我们需要的部分到一个新的切片中:

    func FindDigits(filename string) []byte {
       b, _ := ioutil.ReadFile(filename)
       b = digitRegexp.Find(b)
       c := make([]byte, len(b))
       copy(c, b)
       return c
    }
  • 相关阅读:
    技术总监7年经验——论程序员的职业发展路线
    2.MySQL入门基本操作初体验
    1.MySQL的安装(linux Ubuntu环境下)
    Boot Petalinux Project Using a remote system
    字符设备驱动、平台设备驱动、设备驱动模型、sysfs的比较和关联
    linux采用模块方法,添加一个新的设备
    在远程服务器上完成本地设备的程序烧写和调试(基于vivado ,SDK软件)
    Linux Master/Baremetal Remote 配置下的裸机调试
    利用Xlinix SDK 建立Linux程序以及对该程序进行调试
    Vivado Launching SDK "Importing Hardware Specification" error的解决方法
  • 原文地址:https://www.cnblogs.com/bonelee/p/6862377.html
Copyright © 2011-2022 走看看