Go
函数传参是值类型还是引用类型?
无论如何都是值传递。
基本类型(int, string等):拷贝值
结构体:拷贝值
指针类型:拷贝值,但由于指针指向变量的地址,所以能实现修改值
slice/map/channel:依旧拷贝值,能实现修改值是因为内部有指针指向同一个数组。
比如 slice 的本质是:
1
2
3
4
5type slice struct {
ptr *T // 指针变量共享一个数组
len int
cap int
}
数组和切片的区别?
长度角度:
- 数组长度不可变,在定义时就确定了,
[3]int和[4]int不是一个类型 - 切片长度可变。可使用
append()来扩容切片
- 数组长度不可变,在定义时就确定了,
是否为值类型角度:
数组是值类型,值拷贝
1
2
3
4a := [3]int{1,2,3}
b := a
b[0] = 0
fmt.Println(a) // [1 2 3]切片是引用类型,底层共享数组
1
2
3
4s1 := []int{1,2,3}
s2 := s1
s2[0] = 100
fmt.Println(s1) // [100 2 3]
底层实现角度:
- slice本质是一个结构体,三个属性:数组指针、长度len、容量cap,指针指向了底层数组。切片可以视作数组的窗口(view)。
说说空结构体 struct{}
空结构体的大小:0,也就是说他不占内存空间
1
fmt.Println(unsafe.Sizeof(struct{}{})) // 0我们可以用
struct{}作为 map 的值来模拟一个 set。1
2
3
4
5
6
7
8
9
10
11type 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")
}给管道发送空结构体,代表一种信号,可以节省空间,代替 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 | |
Go
https://becks723.github.io/2026/04/23/Go/