首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Go数据类型,还有哪些你不知道的点?

Go数据类型,还有哪些你不知道的点?

原创
作者头像
闫同学
发布2025-07-27 08:51:49
发布2025-07-27 08:51:49
1390
举报

Go语言以简洁著称,但它的类型系统暗藏玄机。你以为掌握了intstringstruct就万事大吉?引用类型的陷阱、不可比较类型的相关知识,才是真正容易翻车的地方

下面我们就用一篇文章来相对全面的分享一下在Go数据类型中,哪些是引用类型?它们有什么特征?哪些类型可以进行比较?哪些类型不能进行比较?原因是什么?

Go引用类型那些事

在 Go 语言中,严格来说没有传统意义上的“引用类型”,但以下类型的行为类似引用类型(内部隐含指针,操作时共享底层数据),所以一般来讲在Go语言中也就被当做区分与基础类型的引用类型,如: 切片(Slice)、映射(Map)、通道(Channel)、函数(Function)、指针(Pointer)

核心特征

1)赋值/传参不拷贝底层数据 :只复制自身的轻量级结构(如切片的 ptr/len/cap),多个变量共享同一份底层数据

代码语言:go
复制
m1 := map[string]int{"a": 1}
m2 := m1      // 底层哈希表被共享!
m2["a"] = 100 
fmt.Println(m1["a"]) // 输出 100(原值被修改)

2)修改会影响所有引用者 :通过任意变量修改底层数据(如修改切片元素、增删映射键值),其他引用同一数据的变量立即可见变化

  • 内存占用小:变量本身只存储元数据(指针+长度等),大数据的传递开销极低
  • 可与 nil 比较 :零值为 nil,未初始化时默认为 nil(如 var s []int)。
  • 不可用 == 直接比较(除指针) :切片、映射、函数不能直接比较(编译错误),只能与 nil 判等:
代码语言:go
复制
s1 := []int{1, 2}
s2 := []int{1, 2}
fmt.Println(s1 == s2) // 编译错误:slice can only be compared to nil
需要注意的地方

1)切片扩容会“断联”

append 可能触发底层数组重建,新旧切片不再共享数据

代码语言:go
复制
s1 := []int{1, 2}
s2 := s1              // 共享底层数组
s2 = append(s2, 3)    // 容量不足,s2 指向新数组
s2[0] = 100           // 修改新数组
fmt.Println(s1[0])    // 输出 1(原数据未变)

2)map必须显式初始化

nil 值的map不能插入数据(会 panic),需用 make 或字面量初始化。

总的来说,Go 的“引用类型”本质是 结构体 + 内部指针,通过共享底层数据实现高效传递,但需警惕意外修改扩容隔离问题!

Go语言中哪些类型是可比较的?哪些是不可比较的?

在Go语言中,可比较的类型是指那些可以使用==!=运算符进行比较的类型。不可比较的类型则不能使用这些运算符。

可比较类型

基本类型:布尔型(bool)、数值类型(整数、浮点数、复数)、字符串(string)。

指针类型*T):比较的是内存地址。

通道类型chan T):比较的是底层数据结构的地址。如果两个通道是通过同一个make调用创建的,或者都是nil,则它们相等。

接口类型interface{}):比较动态类型和动态值(若动态值不可比较会 panic)。

结构体:如果其所有字段都是可比较的,那么该结构体是可比较的。

数组:如果其元素类型是可比较的,那么该数组是可比较的。

代码语言:go
复制
// 1. 基本类型
b1, b2 := true, false
fmt.Printf("布尔比较: %v\n", b1 == b2) // false

n1, n2 := 10, 10
fmt.Printf("整数比较: %v\n", n1 == n2) // true

// 2. 指针
str := "hello"
p1, p2 := &str, &str
fmt.Printf("指针比较: %v\n", p1 == p2) // true

// 3. 通道
ch1 := make(chan int)
fmt.Printf("通道比较: %v\n", ch1 == nil) // false

// 4. 接口
var i1, i2 interface{} = 42, 42
fmt.Printf("接口比较: %v\n", i1 == i2) // true

// 5. 结构体(所有字段可比较)
type Point struct{ X, Y int }
pA, pB := Point{1, 2}, Point{1, 2}
fmt.Printf("结构体比较: %v\n", pA == pB) // true

// 6. 数组(元素可比较)
arr1 := [2]int{1, 2}
arr2 := [2]int{1, 2}
fmt.Printf("数组比较: %v\n", arr1 == arr2) // true
不可比较类型

切片([]T:切片是引用类型,包含指向底层数组的指针、长度和容量。即使元素相同,底层数组地址可能不同,比较语义不明确,只能与nil比较。

映射(map[K]V:映射也是引用类型,比较需要遍历所有键值对,性能开销大且顺序不确定,只能与nil比较。

函数(func():函数可能包含闭包环境或不同实现,比较无明确定义,只能与nil比较。

包含不可比较字段的结构体或数组:例如:struct { s []int }[2][]int

另外,包含不可比较字段的结构体也是不可比较的。包含不可比较元素的数组也是不可比较的。

代码语言:go
复制
// 1. 切片
slice1 := []int{1, 2}
slice2 := []int{1, 2}
// fmt.Println(slice1 == slice2) // invalid operation

// 2. 映射
m1 := map[string]int{"a": 1}
m2 := map[string]int{"a": }
// fmt.Println(m1 == m2) // invalid operation

// 3. 函数
f1 := func() {}
f2 := func() {}
// fmt.Println(f1 == f2) // invalid operation

// 4. 含切片的结构体
type InvalidStruct struct{ s []int }
v1 := InvalidStruct{s: []int{1}}
v2 := InvalidStruct{s: []int{1}}
// fmt.Println(v1 == v2) // invalid operation

// 5. 接口包含不可比较类型
var iface1 interface{} = []int{1}
var iface2 interface{} = []int{1}
fmt.Println("\n尝试比较接口中的切片:")
fmt.Println(iface1 == iface2) // 运行时panic: comparing uncomparable type []int

在Go语言中,引用类型都是不可比较的吗?

根据Go语言规范,引用类型包括:切片(slice)、映射(map)、通道(channel)、函数(function)以及指针(pointer),其中:

指针(pointer)是可比较的。

通道(channel)是可比较的。

切片(slice)不可比较(除了与nil比较)。

映射(map)不可比较(除了与nil比较)。

函数(function)不可比较(除了与nil比较)。

因此,并非所有引用类型都不可比较。

小总结

Go的类型系统如同冰山。那些看似平凡的切片、映射、函数,实则暗藏"引用共享"的利刃;那些不可比较的类型,更是埋着运行时panic的深雷。

引用类型非真"引用",而是精巧的结构体+指针,共享底层数据的特性带来高效,却也带来"牵一发而动全身"的风险。 看透这些"未知点",你写下的代码将不再摇摇欲坠,而是经得起迭代、协作与时间考验的坚实代码。Go的简洁,从不是偷懒的借口,而是深思熟虑后的优雅。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Go引用类型那些事
    • 核心特征
    • 需要注意的地方
  • Go语言中哪些类型是可比较的?哪些是不可比较的?
    • 可比较类型
    • 不可比较类型
  • 在Go语言中,引用类型都是不可比较的吗?
  • 小总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档