Go基础类

Go 基础类

1、与其他语言相比,使用 Go 有什么好处?

  • 与其他作为学术实验开始的语言不同,Go 代码的设计是务实的。每个功能和语法决策都旨在让程序员的生活更轻松。

  • Golang 针对并发进行了优化,并且在规模上运行良好。

  • 由于单一的标准代码格式,Golang 通常被认为比其他语言更具可读性。

  • 自动垃圾收集明显比 Java 或 Python 更有效,因为它与程序同时执行。

2、Golang 使用什么数据类型?

Golang 使用以下类型:

  • Method

  • Bool

  • String

  • Array

  • Slice

  • Struct

  • Pointer

  • Function

  • Interface

  • Map

  • Channel

3、Go 程序中的包是什么?

包 (pkg) 是 Go 工作区中包含 Go 源文件或其他包的目录。源文件中的每个函 数、变量和类型都存储在链接包中。每个 Go 源文件都属于一个包,该包在文 件顶部使用以下命令声明:

package <packagename>

您可以使用以下方法导入和导出包以重用导出的函数或类型:

import <packagename>

Golang 的标准包是 fmt,其中包含格式化和打印功能,如 Println().

4、Go 支持什么形式的类型转换?将整数转换为浮点数。

Go 支持显式类型转换以满足其严格的类型要求。

i := 55 //int
​
j := 67.8 //float64
​
sum := i + int(j) //j is converted to int

5、什么是 Goroutine?你如何停止它?

一个 Goroutine 是一个函数或方法执行同时旁边其他任何够程采用了特殊的 Goroutine 线程。Goroutine 线程比标准线程更轻量级,大多数 Golang 程序 同时使用数千个 Goroutine。

要创建 Goroutine,请 go 在函数声明之前添加关键字。

go f(x, y, z)

您可以通过向 Goroutine 发送一个信号通道来停止它。Goroutines 只能在被 告知检查时响应信号,因此您需要在逻辑位置(例如 for 循环顶部)包含检 查。

package main
func main() {
 quit := make(chan bool)
 go func() {
     for {
         select {
         case <-quit:
             return
         default:
             // …
         }
     }
}()
// …
quit <- true
}

6、 如何在运行时检查变量类型?

类型开关(Type Switch)是在运行时检查变量类型的最佳方式。类型开关按类型 而不是值来评估变量。每个 Switch 至少包含一个 case 用作条件语句,如果没 有一个 case 为真,则执行 default。

7、Go 两个接口之间可以存在什么关系?

如果两个接口有相同的方法列表,那么他们就是等价的,可以相互赋值。如果 接口 A 的方法列表是接口 B 的方法列表的自己,那么接口 B 可以赋值给接口 A。接口查询是否成功,要在运行期才能够确定。

8、Go 当中同步锁有什么特点?作用是什么

当一个 Goroutine(协程)获得了 Mutex 后,其他 Goroutine(协程)就只能乖 乖的等待,除非该 Goroutine 释放了该 Mutex。RWMutex 在读锁占用的情况下, 会阻止写,但不阻止读 RWMutex。 在写锁占用情况下,会阻止任何其他 Goroutine(无论读和写)进来,整个锁相当于由该 Goroutine 独占 同步锁的作用是保证资源在使用时的独有性,不会因为并发而导致数据错乱, 保证系统的稳定性。

9、Go 语言当中 Channel(通道)有什么特点,需要注意什么?

如果给一个 nil 的 channel 发送数据,会造成永远阻塞。

如果从一个 nil 的 channel 中接收数据,也会造成永久阻塞。

给一个已经关闭的 channel 发送数据, 会引起 panic

从一个已经关闭的 channel 接收数据, 如果缓冲区中为空,则返回一个零值。

10、Go 语言当中 Channel 缓冲有什么特点?

无缓冲的 channel 是同步的,而有缓冲的 channel 是非同步的。

11、Go 语言中 cap 函数可以作用于哪些内容?

可以作用于的类型有:

  • array(数组)

  • slice(切片)

  • channel(通道)

12、Go Convey 是什么?一般用来做什么?

  • go convey 是一个支持 Golang 的单元测试框架

  • go convey 能够自动监控文件修改并启动测试,并可以将测试结果实时输出 到 Web 界面

  • go convey 提供了丰富的断言简化测试用例的编写

13、Go 语言当中 new 的作用是什么?

new 创建一个该类型的实例,并且返回指向该实例的指针。new 函数是内建函 数,函数定义:

func new(Type) *Type
  • 使用 new 函数来分配空间

  • 传递给 new 函数的是一个类型,而不是一个值

  • 返回值是指向这个新分配的地址的指针

14、Go 语言中 make 的作用是什么?

make 的作用是为 slice, map or chan 的初始化 然后返回引用 make 函数是内建函数,函数定义:

func make(Type, size IntegerType) Type

make(T, args)函数的目的和 new(T)不同 仅仅用于创建 slice, map, channel 而且返回类型是实例

15、Printf(),Sprintf(),FprintF() 都是格式化输出,有什么不 同?

  • 虽然这三个函数,都是格式化输出,但是输出的目标不一样 Printf 是标准输出,一般是屏幕,也可以重定向。

  • Sprintf()是把格式化字符串输出到指定的字符串中。

  • Fprintf()是把格式化字符串输出到文件中。

16、Go 语言当中数组和切片的区别是什么?

数组:

  • 数组固定长度。数组长度是数组类型的一部分,所以[3]int 和[4]int 是两种不 同的数组类型数组需要指定大小,不指定也会根据初始化,自动推算出大小, 大小不可改变。数组是通过值传递的

切片:

  • 切片可以改变长度。切片是轻量级的数据结构,三个属性,指针,长度,容量 不需要指定大小切片是地址传递(引用传递)可以通过数组来初始化,也可以 通过内置函数 make()来初始化,初始化的时候 len=cap,然后进行扩容。

17、Go 语言当中值传递和地址传递(引用传递)如何运用?有什 么区别?举例说明

  1. 值传递只会把参数的值复制一份放进对应的函数,两个变量的地址不同, 不可相互修改。

  2. 地址传递(引用传递)会将变量本身传入对应的函数,在函数中可以对该变 量进行值内容的修改。

18、Go 语言当中数组和切片在传递的时候的区别是什么?

  1. 数组是值传递

  2. 切片看上去像是引用传递,但其实是值传递

19、Go 语言是如何实现切片扩容的?

func main() {
 arr := make([]int, 0)
 for i := 0; i < 2000; i++ {
     fmt.Println("len 为", len(arr), "cap 为", cap(arr))
     arr = append(arr, i)
  }
}
我们可以看下结果
依次是
0,1,2,4,8,16,32,64,128,256,512,1024
但到了 1024 之后,就变成了
1024,1280,1696,2304
每次都是扩容了四分之一左右

20、看下面代码的 defer 的执行顺序是什么? defer 的作用和特 点是什么?

defer 的作用是:

你只需要在调用普通函数或方法前加上关键字 defer,就完成了 defer 所需要 的语法。当 defer 语句被执行时,跟在 defer 后面的函数会被延迟执行。直到 包含该 defer 语句的函数执行完毕时,defer 后的函数才会被执行,不论包含 defer 语句的函数是通过 return 正常结束,还是由于 panic 导致的异常结束。 你可以在一个函数中执行多条 defer 语句,它们的执行顺序与声明顺序相反。

defer 的常用场景:

  • defer 语句经常被用于处理成对的操作,如打开、关闭、连接、断开连接、 加锁、释放锁。

  • 通过 defer 机制,不论函数逻辑多复杂,都能保证在任何执行路径下,资 源被释放。

  • 释放资源的 defer 应该直接跟在请求资源的语句后。

21、Golang Slice 的底层实现

切片是基于数组实现的,它的底层是数组,它自己本身非常小,可以理解为对 底层数组的抽象。因为基于数组实现,所以它的底层的内存是连续分配的,效 率非常高,还可以通过索引获得数据。

切片本身并不是动态数组或者数组指针。它内部实现的数据结构通过指针引用 底层数组,设定相关属性将数据读写操作限定在指定的区域内。切片本身是一 个只读对象,其工作机制类似数组指针的一种封装。

切片对象非常小,是因为它是只有 3 个字段的数据结构:

  • 指向底层数组的指针

  • 切片的长度

  • 切片的容量

22、Golang Slice 的扩容机制,有什么注意点?

Go 中切片扩容的策略是这样的:

  • 首先判断,如果新申请容量大于2倍的旧容量,最终容量就是新申请的容量

  • 否则判断,如果旧切片的长度小于 1024,则最终容量就是旧容量的两倍

  • 否则判断,如果旧切片长度大于等于 1024,则最终容量从旧容量开始循环 增加原来的 1/4, 直到最终容量大于等于新申请的容量

  • 如果最终容量计算值溢出,则最终容量就是新申请容量

23、扩容前后的 Slice 是否相同?

情况一:

原数组还有容量可以扩容(实际容量没有填充完),这种情况下,扩容以后的 数组还是指向原来的数组,对一个切片的操作可能影响多个指针指向相同地址 的 Slice。

情况二:

原来数组的容量已经达到了最大值,再想扩容, Go 默认会先开一片内存区 域,把原来的值拷贝过来,然后再执行 append() 操作。这种情况丝毫不影响 原数组。

要复制一个 Slice,最好使用 Copy 函数。

24、Golang 的参数传递、引用类型

Go 语言中所有的传参都是值传递(传值),都是一个副本,一个拷贝。因为拷 贝的内容有时候是非引用类型(int、string、struct 等这些),这样就在函 数中就无法修改原内容数据;有的是引用类型(指针、map、slice、chan 等这 些),这样就可以修改原内容数据。

Golang 的引用类型包括 slice、map 和 channel。它们有复杂的内部结构,除 了申请内存外,还需要初始化相关属性。内置函数 new 计算类型大小,为其分 配零值内存,返回指针。而 make 会被编译器翻译成具体的创建函数,由其分 配内存和初始化成员结构,返回对象而非指针。

25、Golang Map 底层实现

Golang 中 map 的底层实现是一个散列表,因此实现 map 的过程实际上就是实现 散表的过程。在这个散列表中,主要出现的结构体有两个,一个叫 hmap(a header for a go map),一个叫 bmap(a bucket for a Go map,通常叫其 bucket)。

26、Golang Map 如何扩容

  1. Golang Map 如何扩容: 扩容采取了一种称为“渐进式”的方式,原有的 key 并不会一 次性搬迁完毕,每次最多只会搬迁 2 个 bucket。

  2. 等量扩容: 重新排列,极端情况下,重新排列也解决不了,map 存储就会蜕 变成链表,性能大大降低,此时哈希因子 hash0 的设置,可以降低此类极 端场景的发生。

27、Golang Map 查找

Go 语言中 map 采用的是哈希查找表,由一个 key 通过哈希函数得到哈希值,64 位系统中就生成一个 64bit 的哈希值,由这个哈希值将 key 对应存到不同的桶 (bucket)中,当有多个哈希映射到相同的的桶中时,使用链表解决哈希冲 突。

细节:key 经过 hash 后共 64 位,根据 hmap 中 B 的值,计算它到底要落在哪个 桶时,桶的数量为 2^B,如 B=5,那么用 64 位最后 5 位表示第几号桶,在用 hash 值的高 8 位确定在 bucket 中的存储位置,当前 bmap 中的 bucket 未找 到,则查询对应的 overflow bucket,对应位置有数据则对比完整的哈希值, 确定是否是要查找的数据。如果当前 map 处于数据搬移状态,则优先从 oldbuckets 查找。

28、介绍一下 Channel

Go 语言中,不要通过共享内存来通信,而要通过通信来实现内存共享。Go 的 CSP(Communicating Sequential Process)并发模型,中文可以叫做通信顺序进 程,是通过 goroutine 和 channel 来实现的。

channel 收发遵循先进先出 FIFO 的原则。分为有缓冲区和无缓冲区,channel 中包括 buffer、sendx 和 recvx 收发的位置(ring buffer 记录实现)、sendq、 recv。当 channel 因为缓冲区不足而阻塞了队列,则使用双向链表存储。

29、Channel 的 ring buffer 实现

channel 中使用了 ring buffer(环形缓冲区) 来缓存写入的数据。ring buffer 有很多好处,而且非常适合用来实现 FIFO 式的固定长度队列。 在 channel 中,ring buffer 的实现如下:

上图展示的是一个缓冲区为 8 的 channel buffer,recvx 指向最早被读取的数 据,sendx 指向再次写入时插入的位置。

本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 2

「Go学堂」写了一些列的gin框架的底层原理知识。还有相关的使用go语言写的开源包系列。欢迎关注

1年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!