如何判断Golang变量是值类型还是引用类型

来源:编程网作者:小诸葛头衔:草根站长
导读:本期聚焦于小伙伴创作的《如何判断Golang变量是值类型还是引用类型》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《如何判断Golang变量是值类型还是引用类型》有用,将其分享出去将是对创作者最好的鼓励。

Golang的类型系统将变量分为值类型和引用类型两大类,两者的核心差异体现在内存存储和赋值传递的行为上,准确判断变量类型是每个Golang开发者需要掌握的基础能力。

如何判断Golang变量是值类型还是引用类型

值类型与引用类型的定义

值类型

值类型的变量直接存储对应的值,变量在内存中占据的空间大小就是其存储值的大小。当对值类型变量进行赋值、传参操作时,会复制整个变量的值,生成一个新的独立副本,修改新副本不会影响原始变量。

常见的值类型包括:

  • 基本数据类型:int、int8、int16、int32、int64、uint、uint8、uint16、uint32、uint64、float32、float64、complex64、complex128、bool、string
  • 复合类型:数组、结构体(struct)

引用类型

引用类型的变量存储的是指向底层数据的指针,变量本身只占一个指针大小的内存空间,实际数据存储在堆区或其他内存区域。当对引用类型变量进行赋值、传参时,复制的是指针,新变量和原始变量会指向同一份底层数据,修改其中任意一个都会影响另一个。

常见的引用类型包括:

  • 切片(slice)
  • 映射(map)
  • 通道(channel)
  • 函数(func)
  • 指针(*T,虽然指针本身是值类型,但它指向的数据修改会影响所有持有该指针的变量,行为上类似引用)

判断值类型与引用类型的方法

方法一:观察赋值后的修改影响

这是最直观的判断方式,创建变量后复制一份,修改副本的内容,观察原始变量是否发生变化,如果原始变量变化则为引用类型,否则为值类型。

值类型示例:

package main

import "fmt"

func main() {
    // 定义int类型变量,属于值类型
    var a int = 10
    b := a // 赋值操作,复制a的值给b
    b = 20 // 修改b的值
    fmt.Println("a的值:", a) // 输出a的值: 10,原始变量未被修改
    fmt.Println("b的值:", b) // 输出b的值: 20
}

引用类型示例:

package main

import "fmt"

func main() {
    // 定义切片变量,属于引用类型
    s1 := []int{1, 2, 3}
    s2 := s1 // 赋值操作,复制的是切片底层的指针
    s2[0] = 100 // 修改s2的元素
    fmt.Println("s1的值:", s1) // 输出s1的值: [100 2 3],原始变量被修改
    fmt.Println("s2的值:", s2) // 输出s2的值: [100 2 3]
}

方法二:查看类型的零值表现

值类型的零值是其对应类型的默认空值,比如int的零值是0,bool的零值是false;引用类型的零值统一是nil,因为指针未指向任何有效数据。

可以通过声明变量不初始化,打印其零值来判断:

package main

import "fmt"

func main() {
    var i int    // 值类型,零值是0
    var s []int  // 引用类型,零值是nil
    var m map[string]int // 引用类型,零值是nil
    var st struct{} // 值类型,零值是空结构体

    fmt.Printf("int零值: %v, 类型: %Tn", i, i)
    fmt.Printf("slice零值: %v, 类型: %Tn", s, s)
    fmt.Printf("map零值: %v, 类型: %Tn", m, m)
    fmt.Printf("struct零值: %v, 类型: %Tn", st, st)
}

方法三:通过内存地址对比

值类型赋值后,两个变量的内存地址不同;引用类型赋值后,两个变量的指针指向的底层数据地址相同。

package main

import "fmt"

func main() {
    // 值类型数组
    arr1 := [3]int{1,2,3}
    arr2 := arr1
    fmt.Printf("arr1地址: %p, arr2地址: %pn", &arr1, &arr2) // 两个地址不同

    // 引用类型切片
    sl1 := []int{1,2,3}
    sl2 := sl1
    fmt.Printf("sl1底层数据地址: %p, sl2底层数据地址: %pn", sl1, sl2) // 两个地址相同
}

方法四:参考官方类型分类

Golang官方文档中明确划分了类型的归属,对于不确定的类型可以直接查阅官方类型说明,这是最准确的判断依据。需要注意的是string类型比较特殊,它底层是不可变的字节序列,赋值时会复制底层的指针和长度,但由于内容不可变,表现上类似值类型,官方将其归类为值类型。

特殊类型说明

指针类型本身属于值类型,因为指针变量存储的是地址值,赋值时会复制地址值,但通过这个地址可以修改指向的数据。比如*int类型的变量,赋值后两个指针变量是不同的,但它们指向同一个int变量的地址,修改其中一个指针指向的内容会影响另一个。

package main

import "fmt"

func main() {
    a := 10
    p1 := &a
    p2 := p1 // 复制指针值
    *p2 = 20
    fmt.Println("a的值:", a) // 输出20,修改p2指向的内容影响了a
    fmt.Printf("p1地址: %p, p2地址: %pn", &p1, &p2) // p1和p2本身的地址不同
}

Golang值类型引用类型类型系统变量判断修改时间:2026-06-25 08:54:44

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。