导读:本期聚焦于小伙伴创作的《如何在Golang中掌握指针赋值与拷贝_Golang地址传递与值传递区别》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《如何在Golang中掌握指针赋值与拷贝_Golang地址传递与值传递区别》有用,将其分享出去将是对创作者最好的鼓励。

在Golang的开发过程中,理解指针赋值、指针拷贝以及值传递和地址传递的区别,是写出符合预期逻辑代码的基础,很多常见的bug都源于对这些概念的混淆。

如何在Golang中掌握指针赋值与拷贝_Golang地址传递与值传递区别

Golang基础类型回顾

要理解指针相关操作,首先需要明确Golang中的值类型和引用类型的区别。值类型包括int、float、string、struct等,这类类型的变量直接存储值,赋值时会直接拷贝整个值内容。引用类型包括slice、map、channel等,这类类型的变量存储的是指向底层数据结构的指针,赋值时会拷贝这个指针,而不是底层数据本身。

指针赋值与指针拷贝

指针赋值

指针赋值是指将一个指针变量的值(也就是某个变量的内存地址)赋给另一个指针变量,此时两个指针会指向同一个内存地址,修改其中一个指针指向的内容,另一个指针访问到的值也会发生变化。

下面的示例展示了指针赋值的效果:

package main

import "fmt"

func main() {
    // 定义一个int类型变量
    var num int = 10
    // 定义指针p1指向num
    var p1 *int = &num
    // 指针赋值,p2也指向num的内存地址
    var p2 *int = p1

    fmt.Println("修改前:")
    fmt.Printf("num的值:%d, p1指向的值:%d, p2指向的值:%dn", num, *p1, *p2)
    fmt.Printf("p1的地址:%p, p2的地址:%pn", p1, p2)

    // 通过p1修改指向的值
    *p1 = 20
    fmt.Println("通过p1修改后:")
    fmt.Printf("num的值:%d, p1指向的值:%d, p2指向的值:%dn", num, *p1, *p2)
}

运行上述代码可以看到,p1和p2的地址相同,通过p1修改值后,num和p2指向的值都变成了20,这就是指针赋值的特点。

指针拷贝

指针拷贝通常是指拷贝指针指向的内存地址对应的内容,生成一份新的数据,让新的指针指向这份新数据,此时两个指针指向不同的内存地址,修改其中一个指针指向的内容不会影响另一个。

以下是指针拷贝的示例:

package main

import "fmt"

func main() {
    var num int = 10
    var p1 *int = &num
    // 指针拷贝:先拷贝num的值,再让p2指向新的变量
    newNum := *p1
    var p2 *int = &newNum

    fmt.Println("拷贝后初始状态:")
    fmt.Printf("p1指向的值:%d, p2指向的值:%dn", *p1, *p2)
    fmt.Printf("p1的地址:%p, p2的地址:%pn", p1, p2)

    // 修改p1指向的值
    *p1 = 30
    fmt.Println("修改p1指向的值后:")
    fmt.Printf("p1指向的值:%d, p2指向的值:%dn", *p1, *p2)
}

运行结果可以看到,p1和p2的地址不同,修改p1指向的值后,p2指向的值依然是10,说明两者指向的是不同的内存空间。

值传递与地址传递的区别

值传递

值传递是指在函数调用时,将实参的副本传递给函数的形参,函数内部对形参的修改不会影响外部的实参。Golang中默认的参数传递方式就是值传递,无论是值类型还是引用类型,传递的都是对应变量的副本。

值传递的示例:

package main

import "fmt"

// 值传递函数,修改形参的值
func modifyByValue(num int) {
    num = 100
    fmt.Println("函数内部修改后的值:", num)
}

func main() {
    var num int = 10
    fmt.Println("调用函数前的num值:", num)
    modifyByValue(num)
    fmt.Println("调用函数后的num值:", num)
}

运行后可以看到,函数内部修改了num的值,但外部的num依然是10,说明值传递不会影响外部实参。

地址传递

地址传递是指传递变量的内存地址给函数,函数内部通过指针操作修改该地址对应的内容,会直接影响外部的实参。本质上传地址也是值传递的一种,传递的是地址这个值的副本,但因为两个地址指向同一个内存空间,所以修改地址对应的内容会影响外部变量。

地址传递的示例:

package main

import "fmt"

// 地址传递函数,接收指针参数
func modifyByAddress(p *int) {
    *p = 100
    fmt.Println("函数内部修改后的值:", *p)
}

func main() {
    var num int = 10
    fmt.Println("调用函数前的num值:", num)
    modifyByAddress(&num)
    fmt.Println("调用函数后的num值:", num)
}

运行后可以看到,函数内部修改指针指向的值后,外部的num变成了100,说明地址传递可以修改外部实参的值。

两者的适用场景对比

我们可以通过表格清晰对比值传递和地址传递的特点:

对比项值传递地址传递
传递内容实参的副本实参的内存地址副本
对外部实参的影响无影响可以修改实参内容
内存开销值类型开销小,大结构体开销大仅传递地址,开销固定
适用场景不需要修改外部变量,小数据类型需要修改外部变量,大结构体或需要共享数据

常见误区说明

很多开发者会误以为slice、map作为参数传递时是地址传递,实际上Golang中只有值传递,slice和map作为引用类型,传递的是slice结构体的副本,这个结构体里包含了指向底层数组的指针,所以修改slice的元素会影响外部,但修改slice本身的len、cap属性不会影响外部,因为传递的是结构体副本。

以下是验证slice传递特性的示例:

package main

import "fmt"

// 尝试修改slice的长度
func modifySlice(s []int) {
    s = append(s, 4)
    fmt.Println("函数内部slice:", s)
}

func main() {
    s := []int{1, 2, 3}
    fmt.Println("调用函数前的slice:", s)
    modifySlice(s)
    fmt.Println("调用函数后的slice:", s)
}

运行后可以看到,函数内部append后的slice有4个元素,但外部的slice依然是3个元素,说明传递的slice结构体是副本,修改结构体的属性不会影响外部。

总结

掌握Golang的指针赋值、指针拷贝以及值传递和地址传递的区别,核心是要理解内存地址的指向关系。指针赋值是让两个指针指向同一个地址,指针拷贝是生成新的数据和新的指针。值传递传递的是实参副本,不影响外部;地址传递传递的是地址副本,通过地址可以修改外部实参内容。实际开发中可以根据需求选择合适的传递方式,避免因为概念混淆导致程序逻辑错误。

Golang指针赋值指针拷贝值传递地址传递修改时间:2026-07-03 21:06:26

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