GolangRoadmap-Golang面试题笔记(3)

欢迎加入GolangRoadmap,一个年轻的GO开发者社区https://www.golangroadmap.com/,目前是邀请制注册,注册码:Gopher-1035-0722,已开放Golang岗位内推,Golang企业题库,Golang精品资源等栏目

以下题目资源都来自GolangRoadmap->Go求职->企业题库

题目ID-21 Golang Slice 的底层实现
切片是基于数组实现的,它的底层是数组,它自己本身非常小,可以理解为对 底层数组的抽象。因为基于数组实现,所以它的底层的内存是连续分配的,效 率非常高,还可以通过索引获得数据。
切片本身并不是动态数组或者数组指针。它内部实现的数据结构通过指针引用 底层数组,设定相关属性将数据读写操作限定在指定的区域内。切片本身是一 个只读对象,其工作机制类似数组指针的一种封装。
切片对象非常小,是因为它是只有 3 个字段的数据结构:

  • 指向底层数组的指针
  • 切片的长度
  • 切片的容量

题目ID-23 扩容前后的 Slice 是否相同?
slice和map、chan都不太一样的,一样的是,它也可以在函数中修改对应的内容。

func modify(nums []int) {
	fmt.Printf("%p\n", nums)     // num 中真实存储的地址
	fmt.Printf("%p\n", &nums[0]) // 地址nums==&nums[0]==a== &a[0]

	fmt.Printf("%p\n", &nums)    // num 的地址
	nums[0] = 2333
}

func main() {
	a := []int{6, 6, 6}
	fmt.Printf("%p\n", &a)    // slice 的地址
	fmt.Printf("%p\n", a)     // slice 中真实存储的地址
	fmt.Printf("%p\n", &a[0]) // 地址a == &a[0]

	modify(a)
	fmt.Println(a)
}
/*
0xc0000a6020
0xc0000b8000
0xc0000b8000
0xc0000b8000
0xc0000b8000
0xc0000a6060
[2333 6 6]
*/

运行打印结果,发现slice的确是被修改了,而且我们这里打印slice的内存地址是可以直接通过%p打印的,不用使用&取地址符转换。
这就可以证明make的slice也是一个指针了吗?不一定,也可能fmt.Printf把slice特殊处理了。

type slice struct {
	array unsafe.Pointer
	len   int
	cap   int
}
//...
func makeslice(et *_type, len, cap int) unsafe.Pointer {
	//...
}

很明显了,当是slice类型的时候,返回是slice这个结构体里,字段Data第一个元素的地址。

所以我们通过%p打印的slice变量的地址其实就是内部存储数组元素的地址,slice是一种结构体+元素指针的混合类型,通过元素array的指针,可以达到修改slice里存储元素的目的。

所以修改类型的内容的办法有很多种,类型本身作为指针可以,类型里有指针类型的字段也可以。

单纯的从slice这个结构体看,我们可以通过modify修改存储元素的内容,但是永远修改不了len和cap,因为他们只是一个拷贝,不是指针,如果要修改,那就要传递*slice作为参数才可以。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值