Go

函数传参是值类型还是引用类型?

  • 无论如何都是值传递

    • 基本类型(int, string等):拷贝值

    • 结构体:拷贝值

    • 指针类型:拷贝值,但由于指针指向变量的地址,所以能实现修改值

    • slice/map/channel:依旧拷贝值,能实现修改值是因为内部有指针指向同一个数组

      比如 slice 的本质是:

      1
      2
      3
      4
      5
      type slice struct {
      ptr *T // 指针变量共享一个数组
      len int
      cap int
      }

数组和切片的区别?

  • 长度角度:

    • 数组长度不可变,在定义时就确定了,[3]int[4]int 不是一个类型
    • 切片长度可变。可使用 append() 来扩容切片
  • 是否为值类型角度:

    • 数组是值类型,值拷贝

      1
      2
      3
      4
      a := [3]int{1,2,3}
      b := a
      b[0] = 0
      fmt.Println(a) // [1 2 3]
    • 切片是引用类型,底层共享数组

      1
      2
      3
      4
      s1 := []int{1,2,3}
      s2 := s1
      s2[0] = 100
      fmt.Println(s1) // [100 2 3]
  • 底层实现角度:

    • slice本质是一个结构体,三个属性:数组指针、长度len、容量cap,指针指向了底层数组。切片可以视作数组的窗口(view)

说说空结构体 struct{}

  1. 空结构体的大小:0,也就是说他不占内存空间

    1
    fmt.Println(unsafe.Sizeof(struct{}{})) // 0
  2. 我们可以struct{} 作为 map 的值来模拟一个 set

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    type Set map[string]struct{}  // typedef

    set := make(Set)
    // 添加数据
    for _, v := range []string{"a", "a", "b", "c"} {
    set[v] = struct{}{}
    }
    // 检查
    if _, ok := set["a"] {
    fmt.Println("a exists")
    }
  3. 管道发送空结构体,代表一种信号,可以节省空间,代替 bool

    1
    ch <- struct{}{}

init() 函数是什么时候执行的?

  • init() 函数是 golang 中用来初始化包的函数,在 main() 函数之前自动执行,可以放一些字段的初始化逻辑。
  • 一个 go pkg的初始化流程:依赖包 import -> 常量 -> 变量 -> init() -> main()(如果有)
  • 一个包可以有多个init()执行时不保证先后顺序

两个 interface 可以比较吗?

可以。interface可以用 == 比较。一看interface 的动态类型,二看interface 的实际值

  • 一个 interface 内部包含两部分:(type, value)
  • 比较时,一看 动态type:
    • type一致,往下继续看值
    • type不一致,返回 false
    • type一致,但不是“可比较类型”,返回 false
      • 不可比较类型有:slice, map, func 等,channel 可比较
  • 二看 实际value,一致则返回 true:
    • 两个 nil,一致
    • 一个nil一个非nil,不一致
    • 两个值类型(int, string)或结构体内部字段值 相等,一致
    • 两个指针类型,要看是不是指向同一个内存地址,地址相同则一致

几个典型示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
type MyImpl struct {
Name string
}

func main() {
// nil
var nili1 interface{} = nil
var nili2 interface{} = nil
fmt.Println("- nil(non-typed) == nil(non-typed): ", nili1 == nili2) // true

// typed nil
var typedi *int = nil
nili2 = typedi
fmt.Println("- nil(non-typed) == nil(*int): ", nili1 == nili2) // false

// same runtime type, same value
var myImpl1 interface{} = MyImpl{"aaa"}
var myImpl2 interface{} = MyImpl{"aaa"}
fmt.Println("- abc(MyImpl) == abc(MyImpl): ", myImpl1 == myImpl2) // true

// same runtime type, different values
var myImplptr1 interface{} = &MyImpl{"aaa"}
var myImplptr2 interface{} = &MyImpl{"aaa"}
fmt.Println("- abc(*MyImpl) == abc(*MyImpl): ", myImplptr1 == myImplptr2) // false

// uncomparable type
var slice1 interface{} = []int{1, 2, 3}
var slice2 interface{} = []int{1, 2, 3}
fmt.Println("- [1 2 3]([]int) == [1 2 3]([]int): ", slice1 == slice2) // panic
}

Go
https://becks723.github.io/2026/04/23/Go/
作者
Becks723
发布于
2026年4月23日
许可协议