途径1:小白debug

途径2:golang来啦 + go面试每天一篇

途径3:极客时间

基础篇

提问

1.比较性?Struct能不能比较?数组?切片?

2.Slice什么时候扩容?如何扩容?

3.map如何实现顺序读取?

4.值接受者和指针接受者的区别?

5.Go语言函数传参是值类型还是引用类型?

6.Go两个Nil可能不相等吗

7.Go语言中的内存对齐了解吗?

8.两个 interface 可以比较吗?

9.go 打印时 %v %+v %#v 的区别?

10.什么是 rune 类型?

11.空 struct{} 占用空间么?用途?

答案

答案1

  1. 相同struct类型的可以比较
    不同struct类型的不可以比较,编译都不过,类型不匹配
  2. 数组只有相同维数下,才可以进行比较
  3. 切片是引用类型,不可以进行比较

答案2

  1. 什么时候扩容? 在使用 append 向 slice 追加元素时,若 slice 空间不足则会发生扩容
  2. 如何扩容? 1. 扩容会重新分配一块更大的内存,将原 slice 拷贝到新 slice ,然后返回新 slice。+ 扩容后再将数据追加进去。 2. 规律
扩容的时候 实例
cap<1024 cap就会以2倍容量,进行扩容,len就是加多少是多少 看实例里,2,4,8,16,32 足以证明
cap>1024 cap就会以1.25倍,增加容量,len就是加多少是多少

答案3

Go中map如果要实现顺序读取的话,可以先把map中的key,通过sort包排序。
具体代码???

?答案4

究竟在什么情况下才使用指针?参数传递中,值、引用及指针之间的区别!

  1. 方法的接收者: - 值类型,既可以调用值接收者的方法,也可以调用指针接收者的方法; - 指针类型,既可以调用指针接收者的方法,也可以调用值接收者的方法。
  2. 但是接口的实现,值类型接收者和指针类型接收者不一样,原笔记 - 以值类型接收者实现接口,类型本身和该类型的指针类型,都实现了该接口 - 以指针类型接收者实现接口,只有对应的指针类型才被认为实现了接口。
  3. ?通常我们使用指针作为方法的接收者的理由 - 使用指针方法能够修改接收者指向的值。 - 可以避免在每次调用方法时复制该值,在值的类型为大型结构体时,这样做会更加高效。

我没有太理解

?答案5

  • 在Go语言中只存在值传递,要么是值的副本,要么是指针的副本。无论是值类型的变量还是引用类型的变量亦或是指针类型的变量作为参数传递都会发生值拷贝,开辟新的内存空间。
  • 另外值传递、引用传递和值类型、引用类型是两个不同的概念,不要混淆了。引用类型作为变量传递可以影响到函数外部是因为发生值拷贝后新旧变量指向了相同的内存地址。

?答案6

完全不懂,什么是非接口值?T?V?unset?

Go中两个Nil可能不相等。 接口(interface) 是对非接口值(例如指针,struct等)的封装,内部实现包含 2 个字段,类型 T 和 值 V。一个接口等于 nil,当且仅当 T 和 V 处于 unset 状态(T=nil,V is unset)。 两个接口值比较时,会先比较 T,再比较 V。接口值与非接口值比较时,会先将非接口值尝试转换为接口值,再比较。
  1. func main() {
  2. var p *int = nil
  3. var i interface{} = p
  4. fmt.Println(i == p) // true
  5. fmt.Println(p == nil) // true
  6. fmt.Println(i == nil) // false
  7. }
  • 例子中,将一个nil非接口值p赋值给接口i,此时,i的内部字段为(T=*int, V=nil),i与p作比较时,将 p 转换为接口后再比较,因此 i == p,p 与 nil 比较,直接比较值,所以 p == nil。
  • 但是当 i 与nil比较时,会将nil转换为接口(T=nil, V=nil),与i(T=*int, V=nil)不相等,因此 i != nil。因此 V 为 nil ,但 T 不为 nil 的接口不等于 nil。

?答案7

不理解

  1. 什么是内存对齐? CPU 访问内存时,并不是逐个字节访问,而是以字长(word size)为单位访问。比如 32 位的 CPU ,字长为 4 字节,那么 CPU 访问内存的单位也是 4 字节。
  2. 为什么要内存对齐?
    1. ——CPU 始终以字长访问内存,如果不进行内存对齐,很可能增加 CPU 访问内存的次数
    2. 实例:
      面试题 - 图1
      变量 a、b 各占据 3 字节的空间,内存对齐后,a、b 占据 4 字节空间,CPU 读取 b 变量的值只需要进行一次内存访问。如果不进行内存对齐,CPU 读取 b 变量的值需要进行 2 次内存访问。第一次访问得到 b 变量的第 1 个字节,第二次访问得到 b 变量的后两个字节。
  3. 内存对齐优点 1. 也可以看到,内存对齐对实现变量的原子性操作也是有好处的,每次内存访问是原子的,如果变量的大小不超过字长,那么内存对齐后,对该变量的访问就是原子的,这个特性在并发场景下至关重要。 2. 简言之:合理的内存对齐可以提高内存读写的性能,并且便于实现变量操作的原子性。

答案8

这几个函数我记不住

先看类型,类型对应才能比值

  • 判断类型是否一样
    <font style="color:rgb(36, 41, 46);">reflect.TypeOf(a).Kind() == reflect.TypeOf(b).Kind()</font>
  • 判断两个interface{}是否相等
    <font style="color:rgb(36, 41, 46);">reflect.DeepEqual(a, b interface{})</font>
  • 将一个interface{}赋值给另一个interface{}
    <font style="color:rgb(36, 41, 46);">reflect.ValueOf(a).Elem().Set(reflect.ValueOf(b))</font>

答案9

由上至下越来越详细

  • %v 只输出所有的值;
  • %+v 先输出字段名字,再输出该字段的值;
  • %#v 先输出结构体名字值,再输出结构体(字段名字+字段的值);
  1. package main
  2. import "fmt"
  3. type student struct {
  4. id int32
  5. name string
  6. }
  7. func main() {
  8. a := &student{id: 1, name: "微客鸟窝"}
  9. fmt.Printf("a=%v \n", a) // a=&{1 微客鸟窝}
  10. fmt.Printf("a=%+v \n", a) // a=&{id:1 name:微客鸟窝}
  11. fmt.Printf("a=%#v \n", a) // a=&main.student{id:1, name:"微客鸟窝"}
  12. }

答案10

原笔记

Go语言的字符有以下两种:
  • uint8 类型,或者叫 byte 型,代表了 ASCII 码的一个字符。
  • rune 类型,代表一个 UTF-8 字符,当需要处理中文、日文或者其他复合字符时,则需要用到 rune 类型。rune 类型等价于 int32 类型。
  1. package main
  2. import "fmt"
  3. func main() {
  4. var str = "hello 你好" //思考下 len(str) 的长度是多少?
  5. //golang中string底层是通过byte数组实现的,直接求len 实际是在按字节长度计算
  6. //所以一个汉字占3个字节算了3个长度
  7. fmt.Println("len(str):", len(str)) // len(str): 12
  8. //通过rune类型处理unicode字符
  9. fmt.Println("rune:", len([]rune(str))) //rune: 8
  10. }

?答案11

还不能完全理解

  1. 空struct占用空间吗?
    面试题 - 图2 空结构体 struct{} 实例不占据任何的内存空间。
  2. 空struct用途 因为空结构体不占据内存空间,因此被广泛作为各种场景下的占位符使用。 1. 将 map 作为集合(Set)使用时,可以将值类型定义为空结构体,仅作为占位符使用即可。看实例1 2. 不发送数据的信道(channel)
    使用 channel 不需要发送任何的数据,只用来通知子协程(goroutine)执行任务,或只用来控制协程并发度。看实例2
  1. type Set map[string]struct{}
  2. func (s Set) Has(key string) bool {
  3. _, ok := s[key]
  4. return ok
  5. }
  6. func (s Set) Add(key string) {
  7. s[key] = struct{}{}
  8. }
  9. func (s Set) Delete(key string) {
  10. delete(s, key)
  11. }
  12. func main() {
  13. s := make(Set)
  14. s.Add("Tom")
  15. s.Add("Sam")
  16. fmt.Println(s.Has("Tom"))
  17. fmt.Println(s.Has("Jack"))
  18. }
  1. type Door struct{}
  2. func (d Door) Open() {
  3. fmt.Println("Open the door")
  4. }
  5. func (d Door) Close() {
  6. fmt.Println("Close the door")
  7. }

并发