热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

python教程分享Go语言切片常考的面试真题解析

目录前言01.数组和切片有什么区别?02.拷贝大切片一定比拷贝小切片代价大吗?03.切片的深浅拷贝04.零切片、空切片、nil切片是什么05.切片的扩容策略07.参数传递切片和切片
目录
  • 前言
  • 01. 数组和切片有什么区别?
  • 02. 拷贝大切片一定比拷贝小切片代价大吗?
  • 03. 切片的深浅拷贝
  • 04. 零切片、空切片、nil切片是什么
  • 05. 切片的扩容策略
  • 07. 参数传递切片和切片指针有什么区别?
  • 08. range遍历切片有什么要注意的?
  • 总结

前言

哈喽,大家好,我是asong。最近没事在看八股文,总结了几道常考的切片八股文,以问答的方式总结出来,希望对正在面试的你们有用~

python教程分享Go语言切片常考的面试真题解析题目不全,关于切片的面试真题还有哪些?欢迎评论区补充~

01. 数组和切片有什么区别?

go语言中数组是固定长度的,不能动态扩容,在编译期就会确定大小,声明方式如下:

var buffer [255]int  buffer := [255]int{0}  

切片是对数组的抽象,因为数组的长度是不可变的,在某些场景下使用起来就不是很方便,所以go语言提供了一种灵活,功能强悍的内置类型切片("动态数组"),与数组相比切片的长度是不固定的,可以追加元素。切片是一种数据结构,切片不是数组,切片描述的是一块数组,切片结构如下:

Go语言切片常考的面试真题解析

我们可以直接声明一个未指定大小的数组来定义切片,也可以使用make()函数来创建切片,声明方式如下:

var slice []int // 直接声明  slice := []int{1,2,3,4,5} // 字面量方式  slice := make([]int, 5, 10) // make创建  slice := array[1:5] // 截取下标的方式  slice := *new([]int) // new一个  

切片可以使用append追加元素,当cap不足时进行动态扩容。

02. 拷贝大切片一定比拷贝小切片代价大吗?

这道题比较有意思,原文地址:are large slices more expensive than smaller ones?

这道题本质是考察对切片本质的理解,go语言中只有值传递,所以我们以传递切片为例子:

func main()  {   param1 := make([]int, 100)   param2 := make([]int, 100000000)   smallslice(param1)   largeslice(param2)  }    func smallslice(params []int)  {   // ....  }    func largeslice(params []int)  {   // ....  }

切片param2要比param1大1000000个数量级,在进行值拷贝的时候,是否需要更昂贵的操作呢?

实际上不会,因为切片本质内部结构如下:

type sliceheader struct {   data uintptr   len  int   cap  int  }

切片中的第一个字是指向切片底层数组的指针,这是切片的存储空间,第二个字段是切片的长度,第三个字段是容量。将一个切片变量分配给另一个变量只会复制三个机器字,大切片跟小切片的区别无非就是len 和 cap的值比小切片的这两个值大一些,如果发生拷贝,本质上就是拷贝上面的三个字段。

03. 切片的深浅拷贝

深浅拷贝都是进行复制,区别在于复制出来的新对象与原来的对象在它们发生改变时,是否会相互影响,本质区别就是复制出来的对象与原对象是否会指向同一个地址。在go语言,切片拷贝有三种方式:

使用=操作符拷贝切片,这种就是浅拷贝

使用[:]下标的方式复制切片,这种也是浅拷贝

使用go语言的内置函数copy()进行切片拷贝,这种就是深拷贝,

04. 零切片、空切片、nil切片是什么

为什么问题中这么多种切片呢?因为在go语言中切片的创建方式有五种,不同方式创建出来的切片也不一样;

  • 零切片

我们把切片内部数组的元素都是零值或者底层数组的内容就全是 nil的切片叫做零切片,使用make创建的、长度、容量都不为0的切片就是零值切片:

slice := make([]int,5) // 0 0 0 0 0  slice := make([]*int,5) // nil nil nil nil nil  
  • nil切片

nil切片的长度和容量都为0,并且和nil比较的结果为true,采用直接创建切片的方式、new创建切片的方式都可以创建nil切片:

var slice []int  var slice = *new([]int)  
  • 空切片

空切片的长度和容量也都为0,但是和nil的比较结果为false,因为所有的空切片的数据指针都指向同一个地址 0xc42003bda0;使用字面量、make可以创建空切片:

var slice = []int{}  var slice = make([]int, 0)  

空切片指向的 zerobase 内存地址是一个神奇的地址,从 go 语言的源代码中可以看到它的定义:

// base address for all 0-byte allocations  var zerobase uintptr    // 分配对象内存  func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.pointer {   ...   if size == 0 {    return unsafe.pointer(&zerobase)   }    ...  }

05. 切片的扩容策略

这个问题是一个高频考点,我们通过源码来解析一下切片的扩容策略,切片的扩容都是调用growslice方法,截取部分重要源代码:

// runtime/slice.go  // et:表示slice的一个元素;old:表示旧的slice;cap:表示新切片需要的容量;  func growslice(et *_type, old slice, cap int) slice {   if cap  doublecap {    newcap = cap   } else {      // 原 slice 容量小于 1024 的时候,新 slice 容量按2倍扩容    if old.cap <1024 {     newcap = doublecap    } else { // 原 slice 容量超过 1024,新 slice 容量变成原来的1.25倍。     // check 0  maxalloc    newcap = int(capmem)   case et.size == sys.ptrsize:    lenmem = uintptr(old.len) * sys.ptrsize    newlenmem = uintptr(cap) * sys.ptrsize    capmem = roundupsize(uintptr(newcap) * sys.ptrsize)    overflow = uintptr(newcap) > maxalloc/sys.ptrsize    newcap = int(capmem / sys.ptrsize)   case ispoweroftwo(et.size):    var shift uintptr    if sys.ptrsize == 8 {     // mask shift for better code generation.     shift = uintptr(sys.ctz64(uint64(et.size))) & 63    } else {     shift = uintptr(sys.ctz32(uint32(et.size))) & 31    }    lenmem = uintptr(old.len) < (maxalloc >> shift)    newcap = int(capmem >> shift)   default:    lenmem = uintptr(old.len) * et.size    newlenmem = uintptr(cap) * et.size    capmem, overflow = math.muluintptr(et.size, uintptr(newcap))    capmem = roundupsize(capmem)    newcap = int(capmem / et.size)   }  }

通过源代码可以总结切片扩容策略:

切片在扩容时会进行内存对齐,这个和内存分配策略相关。进行内存对齐之后,新 slice 的容量是要 大于等于老 slice 容量的 2倍或者1.25倍,当原 slice 容量小于 1024 的时候,新 slice 容量变成原来的 2 倍;原 slice 容量超过 1024,新 slice 容量变成原来的1.25倍。

07. 参数传递切片和切片指针有什么区别?

我们都知道切片底层就是一个结构体,里面有三个元素:

type sliceheader struct {   data uintptr   len  int   cap  int  }  

分别表示切片底层数据的地址,切片长度,切片容量。

当切片作为参数传递时,其实就是一个结构体的传递,因为go语言参数传递只有值传递,传递一个切片就会浅拷贝原切片,但因为底层数据的地址没有变,所以在函数内对切片的修改,也将会影响到函数外的切片,举例:

func modifyslice(s []string)  {   s[0] = "song"   s[1] = "golang"   fmt.println("out slice: ", s)  }    func main()  {   s := []string{"asong", "golang梦工厂"}   modifyslice(s)   fmt.println("inner slice: ", s)  }  // 运行结果  out slice:  [song golang]  inner slice:  [song golang]

不过这也有一个特例,先看一个例子:

func appendslice(s []string)  {   s = append(s, "快关注!!")   fmt.println("out slice: ", s)  }    func main()  {   s := []string{"asong", "golang梦工厂"}   appendslice(s)   fmt.println("inner slice: ", s)  }  // 运行结果  out slice:  [asong golang梦工厂 快关注!!]  inner slice:  [asong golang梦工厂]

因为切片发生了扩容,函数外的切片指向了一个新的底层数组,所以函数内外不会相互影响,因此可以得出一个结论,当参数直接传递切片时,如果指向底层数组的指针被覆盖或者修改(copy、重分配、append触发扩容),此时函数内部对数据的修改将不再影响到外部的切片,代表长度的len和容量cap也均不会被修改。

参数传递切片指针就很容易理解了,如果你想修改切片中元素的值,并且更改切片的容量和底层数组,则应该按指针传递。

08. range遍历切片有什么要注意的?

go语言提供了range关键字用于for 循环中迭代数组(array)、切片(slice)、通道(channel)或集合(map)的元素,有两种使用方式:

for k,v := range _ { }  for k := range _ { }  

第一种是遍历下标和对应值,第二种是只遍历下标,使用range遍历切片时会先拷贝一份,然后在遍历拷贝数据:

s := []int{1, 2}  for k, v := range s {      }  会被编译器认为是  for_temp := s  len_temp := len(for_temp)  for index_temp := 0; index_temp 

不知道这个知识点的情况下很容易踩坑,例如下面这个例子:

package main    import (   "fmt"  )    type user struct {   name string   age uint64  }    func main()  {   u := []user{    {"asong",23},    {"song",19},    {"asong2020",18},   }   for _,v := range u{    if v.age != 18{     v.age = 20    }   }   fmt.println(u)  }  // 运行结果  [{asong 23} {song 19} {asong2020 18}]

因为使用range遍历切片u,变量v是拷贝切片中的数据,修改拷贝数据不会对原切片有影响。

之前写了一个对for-range踩坑总结,可以读一下:面试官:go中for-range使用过吗?这几个问题你能解释一下原因吗?

总结

python教程分享Go语言切片常考的面试真题解析总结了8道切片相关的面试真题,切片一直是面试中的重要考点,把python教程分享Go语言切片常考的面试真题解析这几个知识点弄会,应对面试官就会变的轻松自如。

到此这篇关于go语言切片面试常考的文章就介绍到这了,更多相关go语言切片面试内容请搜索<编程笔记>以前的文章或继续浏览下面的相关文章希望大家以后多多支持<编程笔记>!

需要了解更多python教程分享Go语言切片常考的面试真题解析,都可以关注python教程分享栏目&#8212;编程笔记


推荐阅读
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • C语言注释工具及快捷键,删除C语言注释工具的实现思路
    本文介绍了C语言中注释的两种方式以及注释的作用,提供了删除C语言注释的工具实现思路,并分享了C语言中注释的快捷键操作方法。 ... [详细]
  • 本文介绍了如何在给定的有序字符序列中插入新字符,并保持序列的有序性。通过示例代码演示了插入过程,以及插入后的字符序列。 ... [详细]
  • 本文介绍了作者在开发过程中遇到的问题,即播放框架内容安全策略设置不起作用的错误。作者通过使用编译时依赖注入的方式解决了这个问题,并分享了解决方案。文章详细描述了问题的出现情况、错误输出内容以及解决方案的具体步骤。如果你也遇到了类似的问题,本文可能对你有一定的参考价值。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • 开发笔记:计网局域网:NAT 是如何工作的?
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了计网-局域网:NAT是如何工作的?相关的知识,希望对你有一定的参考价值。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • Go Cobra命令行工具入门教程
    本文介绍了Go语言实现的命令行工具Cobra的基本概念、安装方法和入门实践。Cobra被广泛应用于各种项目中,如Kubernetes、Hugo和Github CLI等。通过使用Cobra,我们可以快速创建命令行工具,适用于写测试脚本和各种服务的Admin CLI。文章还通过一个简单的demo演示了Cobra的使用方法。 ... [详细]
  • 怎么在PHP项目中实现一个HTTP断点续传功能发布时间:2021-01-1916:26:06来源:亿速云阅读:96作者:Le ... [详细]
  • 多维数组的使用
    本文介绍了多维数组的概念和使用方法,以及二维数组的特点和操作方式。同时还介绍了如何获取数组的长度。 ... [详细]
  • 本文讨论了在手机移动端如何使用HTML5和JavaScript实现视频上传并压缩视频质量,或者降低手机摄像头拍摄质量的问题。作者指出HTML5和JavaScript无法直接压缩视频,只能通过将视频传送到服务器端由后端进行压缩。对于控制相机拍摄质量,只有使用JAVA编写Android客户端才能实现压缩。此外,作者还解释了在交作业时使用zip格式压缩包导致CSS文件和图片音乐丢失的原因,并提供了解决方法。最后,作者还介绍了一个用于处理图片的类,可以实现图片剪裁处理和生成缩略图的功能。 ... [详细]
  • Java中包装类的设计原因以及操作方法
    本文主要介绍了Java中设计包装类的原因以及操作方法。在Java中,除了对象类型,还有八大基本类型,为了将基本类型转换成对象,Java引入了包装类。文章通过介绍包装类的定义和实现,解答了为什么需要包装类的问题,并提供了简单易用的操作方法。通过本文的学习,读者可以更好地理解和应用Java中的包装类。 ... [详细]
  • Java自带的观察者模式及实现方法详解
    本文介绍了Java自带的观察者模式,包括Observer和Observable对象的定义和使用方法。通过添加观察者和设置内部标志位,当被观察者中的事件发生变化时,通知观察者对象并执行相应的操作。实现观察者模式非常简单,只需继承Observable类和实现Observer接口即可。详情请参考Java官方api文档。 ... [详细]
author-avatar
罂粟花的美童鞋
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有