当前位置: 首页 > article >正文

slice

slic

文章目录

  • slic
    • 1.切片通过函数,传的是什么?
    • 2.在函数里面改变切片,函数外的切片会改变吗?
    • 3.截取切片
    • 4.删除元素
    • 5.新增元素
    • 6.深度拷贝
    • 7.面试题

1.切片通过函数,传的是什么?

package main

import (
	"fmt"
	"reflect"
	"unsafe"
)

func main() {
	s := make([]int, 5, 10)
	printSlice(&s)
	test(s)
}

func test(i []int) {
	printSlice(&i)
}

func printSlice(s *[]int) {
	/*
		reflect.SliceHeader:用来描述切片的底层实现
		unsafe.Pointer:是一个通用类型的指针,可以用作不同类型指针相互转换
	*/
	ss := (*reflect.SliceHeader)(unsafe.Pointer(s))
	fmt.Printf("%+v\n", ss)
}

运行结果:

&{Data:824633827808 Len:5 Cap:10}
&{Data:824633827808 Len:5 Cap:10}

2.在函数里面改变切片,函数外的切片会改变吗?

1.切片做参数传递,修改切片内容影响原切片。

package main

import (
	"fmt"
	"reflect"
	"unsafe"
)

func main() {
	s := make([]int, 5)
	case1(s)
	printSliceStruct(&s)
}

func case1(s []int) {
	s[1] = 1
	printSliceStruct(&s)
}

func printSliceStruct(s *[]int) {
	/*
		reflect.SliceHeader:用来描述切片的底层实现
		unsafe.Pointer:是一个通用类型的指针,可以用作不同类型指针相互转换
	*/
	ss := (*reflect.SliceHeader)(unsafe.Pointer(s))
	fmt.Printf("%+v,%v\n", ss, s)
}

输出结果:

&{Data:824633779184 Len:5 Cap:5},&[0 1 0 0 0]
&{Data:824633779184 Len:5 Cap:5},&[0 1 0 0 0]

2.引用参数传递的时候,在另个函数内部扩容了,那么就会分配新的内存地址,再修改这个切片就不会影响原切片了。

具体点可以这样来理解:append切片的时候如果容量还没满,那么这个新切片只是引用原来的底层数组,只不过是长度不一样,可以说是返回的就是原切片;如果容量已满那么扩容后返回的就是一个新切片。

package main

import (
	"fmt"
	"reflect"
	"unsafe"
)

func main() {
	s := make([]int, 5)
	case2(s)
	printSliceStruct(&s)
}

func case2(s []int) {
	s = append(s, 0)
	s[1] = 1
	printSliceStruct(&s)
}

func printSliceStruct(s *[]int) {
	/*
		reflect.SliceHeader:用来描述切片的底层实现
		unsafe.Pointer:是一个通用类型的指针,可以用作不同类型指针相互转换
	*/
	ss := (*reflect.SliceHeader)(unsafe.Pointer(s))
	fmt.Printf("%+v,%v\n", ss, s)
}

输出结果:

&{Data:824633827808 Len:6 Cap:10},&[0 1 0 0 0 0]
&{Data:824633779184 Len:5 Cap:5},&[0 0 0 0 0]

总结:传递切片的时候如果切片的修改要反应外部最好使用指针。就是说,如果传值,那么扩容前可以反应到外部,一旦扩容就不再影响外部,传指针的话,无论是否扩容,指针都能追踪上。

以下是这个案例:

package main

import (
	"fmt"
	"reflect"
	"unsafe"
)

func main() {
	s := make([]int, 5)
	//case1(s)
	//case2(s)
	case3(&s)
	printSliceStruct(&s)
}
func case3(s *[]int) {
	*s = append(*s, 0)
	(*s)[1] = 1
	printSliceStruct(s)
}
func printSliceStruct(s *[]int) {
	/*
		reflect.SliceHeader:用来描述切片的底层实现
		unsafe.Pointer:是一个通用类型的指针,可以用作不同类型指针相互转换
	*/
	ss := (*reflect.SliceHeader)(unsafe.Pointer(s))
	fmt.Printf("%+v,%v\n", ss, s)
}

输出结果:

&{Data:824634425344 Len:6 Cap:10},&[0 1 0 0 0 0]
&{Data:824634425344 Len:6 Cap:10},&[0 1 0 0 0 0]

3.截取切片

问:通过:操作得到的新切片和原切片是什么关系?

答:截取切片只是修改了底层数组的起始指向和末尾指向,长度为截取长度,容量为起始指向到末尾指向的大小。用截取切片初始化新切片也是值传递。

4.删除元素

package main

import (
	"fmt"
	"reflect"
	"unsafe"
)

func main() {
	s := []int{0, 1, 2, 3, 4}
	printSliceStruct(&s)

	s1 := append(s[:1], s[2:]...)

	printSliceStruct(&s1)
	printSliceStruct(&s)
}

func printSliceStruct(s *[]int) {
	/*
		reflect.SliceHeader:用来描述切片的底层实现
		unsafe.Pointer:是一个通用类型的指针,可以用作不同类型指针相互转换
	*/
	ss := (*reflect.SliceHeader)(unsafe.Pointer(s))
	fmt.Printf("%+v,%v\n", ss, s)
}

输出结果:

&{Data:824633779184 Len:5 Cap:5},&[0 1 2 3 4]
&{Data:824633779184 Len:4 Cap:5},&[0 2 3 4]
&{Data:824633779184 Len:5 Cap:5},&[0 2 3 4 4]

分析:

切片没有内置的删除函数,而是使用append函数实现删除的功能。

第二个print打印和第三个print打印相比之下,可以知道append函数实现的删除不是真的删除,而是后边的数据向前覆盖,这也导致最后的数据没有改变。

5.新增元素

Golang中新增元素就是使用append函数,有两种情况:

  1. 切片的底层数组容量足够。那么,使用append添加新元素后的切片与元切片共享底层数组。
  2. 切片的底层数组容量不足。那么,append会返回一个新的底层数组。
package main

import (
	"fmt"
	"reflect"
	"unsafe"
)

func main() {
	case1()
}

func case1() {
	sc := make([]int, 3, 5)
	printSliceStruct(&sc)
	s1 := append(sc, 1)
	printSliceStruct(&sc)
	printSliceStruct(&s1)
	s1[0] = 10
	printSliceStruct(&sc)
	printSliceStruct(&s1)
	fmt.Println("--")
	s2 := append(sc, 1, 2, 3, 4, 5, 6)
	printSliceStruct(&sc)
	printSliceStruct(&s2)
	s2[2] = 20
	printSliceStruct(&sc)
	printSliceStruct(&s1)
}

func printSliceStruct(s *[]int) {
	/*
		reflect.SliceHeader:用来描述切片的底层实现
		unsafe.Pointer:是一个通用类型的指针,可以用作不同类型指针相互转换
	*/
	ss := (*reflect.SliceHeader)(unsafe.Pointer(s))
	fmt.Printf("%+v,%v\n", ss, s)
}

输出结果:

&{Data:824633779184 Len:3 Cap:5},&[0 0 0]
&{Data:824633779184 Len:3 Cap:5},&[0 0 0]
&{Data:824633779184 Len:4 Cap:5},&[0 0 0 1]
&{Data:824633779184 Len:3 Cap:5},&[10 0 0]
&{Data:824633779184 Len:4 Cap:5},&[10 0 0 1]
--
&{Data:824633779184 Len:3 Cap:5},&[10 0 0]
&{Data:824633827808 Len:9 Cap:10},&[10 0 0 1 2 3 4 5 6]
&{Data:824633779184 Len:3 Cap:5},&[10 0 0]
&{Data:824633779184 Len:4 Cap:5},&[10 0 0 1]

6.深度拷贝

Golang中切片的传递,底层数组是浅拷贝(操作原来切片会影响新切片),那么有没有深度拷贝的方法呢?

package main

import (
	"fmt"
	"reflect"
	"unsafe"
)

func main() {
	case1()
}

func case1() {
	var s1 = []int{1, 2, 3, 4}
	var s2 = make([]int, 5)
	copy(s2, s1)//按长度最短的copy
	printSliceStruct(&s1)
	printSliceStruct(&s2)
}

func printSliceStruct(s *[]int) {
	/*
		reflect.SliceHeader:用来描述切片的底层实现
		unsafe.Pointer:是一个通用类型的指针,可以用作不同类型指针相互转换
	*/
	ss := (*reflect.SliceHeader)(unsafe.Pointer(s))
	fmt.Printf("%+v,%v\n", ss, s)
}

输出结果:

&{Data:824633803200 Len:4 Cap:4},&[1 2 3 4]
&{Data:824633779184 Len:5 Cap:5},&[1 2 3 4 0]

7.面试题

package main

import (
	"fmt"
)

func main() {
	doAppend := func(s []int) {
		s = append(s, 1)
		printSliceStruct(s)
	}
	s := make([]int, 8, 8)
	doAppend(s[:4])
	printSliceStruct(s)
	doAppend(s)
	printSliceStruct(s)
}

func printSliceStruct(s []int) {
	fmt.Println(s)
	fmt.Printf("len=%d cap=%d\n", len(s), cap(s))
}

输出结果:

[0 0 0 0 1]
len=5 cap=8
[0 0 0 0 1 0 0 0]
len=8 cap=8
[0 0 0 0 1 0 0 0 1]
len=9 cap=16
[0 0 0 0 1 0 0 0]
len=8 cap=8

http://www.kler.cn/a/290498.html

相关文章:

  • 现代Web开发:Vue 3 组件化开发实战
  • 云计算:定义、类型及对企业的影响
  • 【非关系型数据库】【IOT设备】InfluxDB、TimescaleDB、Cassandra和MongoDB
  • 【算法一周目】双指针(1)
  • 新的服务器Centos7.6 安卓基础的环境配置(新服务器可直接粘贴使用配置)
  • 2024HNCPC G - Utakotoba
  • 一篇文章深入了解Oracle常用命令
  • xxe漏洞
  • 小型集群分析
  • 【IPV6从入门到起飞】3-域名解析动态IPV6(阿里云)
  • 学习大数据DAY49 考后练习题
  • python学习8:dict字典的定义,操作和方法,跟json有什么区别?
  • 通过查找真实IP bypass WAF
  • 容器化你的应用:使用 Docker 入门指南
  • window 10使用wsl装docker踩坑
  • CCNA课笔记
  • 【NO.17】LeetCode经典150题-13. 罗马数字转整数
  • 装机必看!内存条应该怎么插?各种内存条插法详解
  • Seataf分布式事务的使用
  • 坐牢第三十四天(c++)
  • WordPress自适应美图网站整站打包源码
  • Docker镜像中的源替换为国内源
  • C/C++:函数指针
  • SpringBoot3集成Spring Authorization Server实现SSO单点登录
  • uniapp小程序实现横屏手写签名
  • 宠物智能家居监测器的融合