Golang基础常识性面试中常见的六大陷阱及应对技巧总结

 更新时间:2024年08月06日 09:53:53   作者:少林码僧  
Go是一门简单有趣的语言,但与其他语言类似,它会有一些技巧,这篇文章主要给大家介绍了关于Golang基础常识性面试中常见的六大陷阱及应对技巧的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下

一、nil slice & empty slice

1、nil切片与空切片底层

  • nil切片:var nilSlice [] string

    • nil slice的长度len和容量cap都是0

    • nil slice==nil

    • nil slice的pointer是nil

  • 空切片:emptySlice0 := make([]int,0)

    • empty slice的长度是0,容量是由指向底层数组决定

    • empty slice != nil

    • empty slice的pointer是底层数组的地址

  • nil切片和空切片最大的区别在指向的数组引用地址是不一样的

  • nil空切片引用数组指针地址为0(无指向任何实际地址)

图片

  • 空切片的引用数组指针地址是有的,且固定为一个值,所有的空切片指向的数组引用地址都是一样的

图片

2、创建nil slice 和empty slice

package main
import "fmt"
func main() {
  var nilSlice []string  // 创建一个 nil 切片
  emptySlice0 := make([]int, 0)  // 方法1:创建一个空切片(零切片)
  var emptySlice1 = []string{}   // 方法2:创建一个空切片
  fmt.Printf("\nnilSlice---> Nil:%v Len:%d Capacity:%d", nilSlice == nil, len(nilSlice), cap(nilSlice))
  fmt.Printf("\nemptySlice0---> nil:%v Len:%d Capacity:%d", emptySlice0 == nil, len(emptySlice0), cap(emptySlice0))
  fmt.Printf("\nemptySlice1---> nil:%v Len:%d Capacity:%d", emptySlice1 == nil, len(emptySlice1), cap(emptySlice1))
  // nil切片和空切片都可以正常 append数据
  nilSlice = append(nilSlice, "sss")
}
/*
Nil:true Len:0 Capacity:0
nil:false Len:0 Capacity:0
nil:false Len:0 Capacity:0[sss]
 */

二、类型强转产生内存拷贝

1、字符串转数组发送内存拷贝

  • 字符串转成byte数组,会发生内存拷贝吗?

  • 字符串转出切片,会产生拷贝

  • 严格来说,只要是发送类型强转都会发送内存拷贝

  • 那么问题来了,频繁的内存拷贝操作听起来对性能不大友好

  • 有没有什么办法可以在字符串转出切片的时候不用发生拷贝呢?

2、字符串转数组不内存拷贝方法

  • 那么如果想要在底层转换二者,只需要吧StringHeader的地址强转成SliceHeader就行,那么go有个很强的包叫unsafe

  • 1.unsafe.Pointer(&a)方法可以得到变量a的地址。

    • 2.(*reflect.StringHeader)(unsafe.Pointer(&a)) 可以把字符串a转成底层结构的形式。

    • 3.(*[]byte)(unsafe.Pointer(&ssh)) 可以把ssh底层结构体转成byte的切片的指针。

    • 4.再通过 *转为指针指向的实际内容。

package main

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

func main() {
   a :="aaa"
   ssh := *(*reflect.StringHeader)(unsafe.Pointer(&a))
   b := *(*[]byte)(unsafe.Pointer(&ssh))
   fmt.Printf("%v---%T",b,b)  // [97 97 97]---[]uint8
}

三、拷贝大切片一定代价大吗?

  • SliceHeader 是切片在go的底层结构。

    • 第一个字是指向切片底层数组的指针,这是切片的存储空间

    • 第二个字段是切片的长度

    • 第三个字段是容量

type SliceHeader struct {
  Data uintptr
  Len  int
  Cap  int
}
  • 大切片跟小切片的区别无非就是 Len 和 Cap的值比小切片的这两个值大一些,如果发生拷贝,本质上就是拷贝上面的三个字段。

  • 所以 拷贝大切片跟小切片的代价应该是一样的

四、map不初始化使用会怎么样

  • 空map和nil map结果是一样的,都为map[]。

  • 所以,这个时候别断定map是空还是nil,而应该通过map == nil来判断。

package main

func main() {
  var m1 map[string]string    // 创建一个 nil map
  println("m1为nil: ", m1==nil)
  // 报错 => panic: assignment to entry in nil map
  //m1["name"] = "tom"

  var m2 =  make(map[string]string)  // 创建一个空map
  m2["name"] = "jack"                // 空map可以正常
  println("m2为nil: ", m2==nil)
}

五、map会遍历删除安全吗?

  • map 并不是一个线程安全的数据结构。

  • 同时读写一个 map 是未定义的行为,如果被检测到,会直接 panic。

  • 上面说的是发生在多个协程同时读写同一个 map 的情况下。

  • 如果在同一个协程内边遍历边删除,并不会检测到同时读写,理论上是可以这样做的。

  • sync.Map可以解决多线程读写map问题

    • 一般而言,这可以通过读写锁来解决:sync.RWMutex

    • 读之前调用 RLock() 函数,读完之后调用 RUnlock() 函数解锁;

    • 写之前调用 Lock() 函数,写完之后,调用 Unlock() 解锁。

    • 另外,sync.Map 是线程安全的 map,也可以使用

六、for循环append坑

1、坑1:添加元素变覆盖

  • 不会死循环,for range其实是golang语法糖,在循环开始前会获取切片的长度 len(切片),然后再执行len(切片)次数的循环。

package main
import "fmt"
func main() {
  s := []int{1,2,3,4,5}
  for _, v:=range s {
    s =append(s, v)
    fmt.Printf("len(s)=%v\n",len(s))
  }
}
/*
len(s)=6
len(s)=7
len(s)=8
len(s)=9
len(s)=10
 */

2、坑2:值全部一样

  • 每次循转中num的值是正常的,但是由append构造的res中,全是nums的最后一个值。

  • 最终总结出原因是在for range语句中,创建了变量num且只被创建了一次。

  • 即num有自己的空间内存且地址在for循环过程中不变

  • 循环过程中每次将nums中对应的值和num进行值传递

package main
import "fmt"
func main() {
  var nums = []int{1, 2, 3, 4, 5}
  var res []*int
  for _, num := range nums {
    res = append(res, &num)
    //fmt.Println("num:", num)
  }
  for _, r := range res {
    fmt.Println("res:", *r)
  }
}
/*
res: 5
res: 5
res: 5
res: 5
res: 5
 */

3、解决方法

  • 方法1

    • 不使用for range的形式,直接用索引来对nums取值

package main
import "fmt"
func main() {
  var nums = []int{1, 2, 3, 4, 5}
  var res []*int
  for i := 0; i < len(nums); i++ {
    res = append(res, &nums[i])
  }
  fmt.Println("res:", res)
  for _, r := range res {
    fmt.Println("res:", *r)
  }
}
  • 方法2

    • 在for循环中每次再定义一个新的变量num_temp,将num的值传给num_temp,之后append该变量即可。

package main
import "fmt"
func main() {
  var nums = []int{1, 2, 3, 4, 5}
  var res []*int
  for _, num := range nums {
    numTemp := num // 创建一个新的临时变量
    res = append(res, &numTemp)
  }
  for _, r := range res {
    fmt.Println("res:", *r)
  }
}
/*
res: 1
res: 2
res: 3
res: 4
res: 5
 */

总结

到此这篇关于Golang基础常识性面试中常见的六大陷阱及应对技巧总结的文章就介绍到这了,更多相关Golang面试常见陷阱及应对内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • golang使用jaeger进行链路追踪

    golang使用jaeger进行链路追踪

    链路追踪是指在分布式系统中,将一次请求的处理过程进行记录并聚合展示的一种方法,目的是将一次分布式请求的调用情况集中在一处展示,本文将介绍golang如何使用jaeger进行链路追踪,需要的朋友可以参考下
    2024-06-06
  • Go语言循环遍历含有中文的字符串的方法小结

    Go语言循环遍历含有中文的字符串的方法小结

    这篇文章主要介绍了Go语言循环遍历含有中文的字符串的几种方法,文章通过代码示例讲解的非常详细,具有一定的参考价值,感兴趣的小伙伴跟着小编一起来看看吧
    2023-07-07
  • 一百行Golang代码实现简单并发聊天室

    一百行Golang代码实现简单并发聊天室

    这篇文章主要为大家详细介绍了一百行Golang代码如何实现简单并发聊天室,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-08-08
  • Go语言删除文本文件中的指定行操作代码

    Go语言删除文本文件中的指定行操作代码

    假设现在有一个文本文件,我们需要删除文件中乱码的行。我们可以使用go的os库来处理文件,遍历整个文件然后将除过乱码的行写入一个新文件,以此来实现我们的需求,这篇文章主要介绍了Go语言删除文本文件中的指定行,需要的朋友可以参考下
    2023-12-12
  • 基于Golang实现Redis分布式锁解决秒杀问题

    基于Golang实现Redis分布式锁解决秒杀问题

    这篇文章主要给大家介绍了使用Golang实现Redis分布式锁解决秒杀问题,文中有详细的代码示例供大家参考,具有一定的参考价值,需要的朋友可以参考下
    2023-08-08
  • 通过goland 进行远程 dlv的实现过程

    通过goland 进行远程 dlv的实现过程

    本文探讨Kubernetes中通过Remote dlv调试解决容器因远程数据源异常导致的问题难以复现,采用Sidecar和直接集成方式,配置attach、--headless等参数,并利用本地端口转发实现调试连接,感兴趣的朋友跟随小编一起看看吧
    2025-09-09
  • 深入探索Go语言中的高效数据结构堆

    深入探索Go语言中的高效数据结构堆

    堆,作为一种基本的数据结构,以其在优先队列和排序算法中提供高效解决方案的能力而闻名。在本文中,我们将深入探讨堆的内部工作原理,包括其特性、实现细节以及在现代编程中的应用
    2008-06-06
  • 详解golang中bufio包的实现原理

    详解golang中bufio包的实现原理

    这篇文章主要介绍了详解golang中bufio包的实现原理,通过分析golang中bufio包的源码,来了解为什么bufio能够提高文件读写的效率和速度
    2018-01-01
  • Go计时器的示例代码

    Go计时器的示例代码

    定时器是任何编程语言的重要工具,它允许开发人员在特定时间间隔安排任务或执行代码,本文主要介绍了Go计时器的示例代码,具有一定的参考价值,感兴趣的可以了解一下
    2024-01-01
  • golang中for range的取地址操作陷阱介绍

    golang中for range的取地址操作陷阱介绍

    这篇文章主要介绍了golang中for range的取地址操作陷阱,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-04-04

最新评论