go中值传递和指针传递的使用

 更新时间:2024年10月16日 11:36:27   作者:-代号9527  
在Go语言中,使用&和*可以分别取得变量的地址和值,解引用未初始化或为nil的指针会引发空指针异常,正确的做法是先进行nil检查,此外,nil在Go中用于多种类型的空值表示,值传递和指针传递各有适用场景,通常小型数据结构优先考虑值传递以减少解引用开销

1、& 和 *

  • &后跟一个变量名,得到的是这个变量的内存地址
  • *int类型的变量,代表这个变量里存的值是int类型的变量的内存地址
  • 数据类型的指针类型,即在其前面加 * 号
  • 指针就是内存地址
package main
import(
        "fmt"
)
func main(){

        var age int = 18
        //&符号+变量 就可以获取这个变量内存的地址
        fmt.Println(&age) //0xc0000a2058
        
        //ptr是一个变量,自身也有内存地址
        //&age就是一个地址,是ptr变量的具体的值
        var ptr *int = &age

		//这样直接输出,是ptr这个指针变量的值,即0xc0000a2058
        fmt.Println(ptr)
        
        //ptr这个指针变量自身的地址
        fmt.Println("ptr本身这个存储空间的地址为:",&ptr)
        
        //想获取ptr这个指针或者这个地址指向的那个数据:
        fmt.Printf("ptr指向的数值为:%v",*ptr) //ptr指向的数值为:18
}


在这里插入图片描述

对指针类型的变量再加*,是取真实值,即解引用

x := 10
a := &x // 取变量x的地址,a是一个指向int的指针 (*int 类型)

fmt.Println(*a) // 输出a指向的整数值,即变量x的值,这里将输出 10

2、空指针

* 虽然可以取到指针类型的真实值(解引用),但对nil解引用,会空指针:panic: runtime error: invalid memory address or nil pointer dereference

比如以下情况:

声明了一个指针变量,未初始化就直接解引用

var a *int
fmt.Println(*a)  // 这里将会导致空指针错误

给一个指针变量赋值nil后解引用

var a *int = nil
fmt.Println(*a)  // 这里将会导致空指针错误

调用了一个返回值是指针类型,但返回结果是nil的函数。此时直接解引用会空指针。

func returnNilPointer() *int {
    return nil
}

func main() {
    var a *int = returnNilPointer()
    fmt.Println(*a)  // 这里将会导致空指针错误
}

对指针类型解引用的正确做法是,先判空:

var ptr *int

if ptr != nil {
    fmt.Println(*ptr)  // 安全地解引用ptr
} else {
    fmt.Println("Pointer is nil")
    // 避免解引用nil指针
}

3、nil

源码:

在这里插入图片描述

  • nil是go语言SDK中预先定义好的
  • 可以使用 == 操作符来比较指针、切片、映射、通道和接口变量是否为 nil
  • nil是指针、接口、切片、映射、通道和函数类型的空值
// 指针
var ptr *int
fmt.Println(ptr)  // 输出: nil,即不指向任何有效的内存地址
// 接口
var iface fmt.Stringer
fmt.Println(iface == nil)  // 输出: true,即接口变量不指向任何具体的实现类对象
// 切片
var s []int
fmt.Println(s == nil)  // 输出: true,即表示一个空切片,即长度和容量都为0的切片
// 映射
var m map[string]int
fmt.Println(m == nil)  // 输出: true,表示一个空映射,即不包含任何键值对的映射
// 通道
var ch chan int
fmt.Println(ch == nil)  // 输出: true,即未初始化的通道默认为 nil
// 定义一个函数类型 HandlerFunc
type HandlerFunc func(int) string

// 声明一个 HandlerFunc 类型的变量 handler,但未赋值,其值为nil
 var handler HandlerFunc

因此,在未初始化的通道中发送或者接口数据、在未初始化的map中进行存储或者取值,就会panic,但切片有一点不同

var ch chan int
ch <- 1  // 尝试向空通道发送数据会导致panic

报错:

var m map[string]int
m["key"] = 1  // 尝试在空映射中存储值会导致panic

报错:

只定义,未初始化的切片,其值为nil,表示一个空切片,即长度和容量都为0的切片,此时,直接s[0] = 1就会发生下标越界panic,但用append方法一切正常,append 函数会根据需要自动初始化切片并分配内存

var s []int
s[0] = 1	// 越界panic

在这里插入图片描述

var s []int
s = append(s, 1)	// 不会panic或者空指针

注意,自定义的结构体的空值不是nil,这一点和Java中的null不一样(但如果加了&,即取地址,那就是自定义结构体的指针类型,其空值为nil)

在这里插入图片描述

此外,go中,所有的变量 (包括结构体变量) 在声明时如果没有显式赋值,会被赋予其类型的零值。比如:

在这里插入图片描述

4、用值传递还是指针传递?

什么时候用值传递,什么时候用指针传递?比如向函数调用栈里的下一个方法传递对象A,二者的区别在于,指针传递,传的是对象A的内存地址,传的是一个小巧的地址。值传递,是复制对象A的数据传下去。

之前有个说法:想在调用的函数内部改变对象A的值,就用指针传递,但这句话也不全对,因为用值传递,照样可以实现同样的效果。比如对象A为:

// 矩形
type Rectangle struct {
	Width, Height int
}

此时,要改变矩形的宽,值传递和引用传递(指针传递)的实现如下:

在这里插入图片描述

注意看二者的返回值,值传递,因为不能直接修改原对象,因此,需要将副本对象整个都返回,引用传递,则是一个void方法,因为其接收一个原对象的内存地址,可以直接修改原对象。这两种实现方式,在此时,没有谁优谁劣。

考虑优先使用值传递,原因如下:

  • 对于固定大小的类型(整数、浮点数、小型结构体、小型数组),它们占用的内存大小固定且小,大小与指针大小相当

  • 值传递,代表的意志是:函数收到的是一个副本数据,我只是需要操作这份数据,不会改动你的原始数据

在这里插入图片描述

  • 对于较小的对象,直接值传递,可以避免引用传递时对指针解引用的额外步骤

此外,从底层分析原因:

在这里插入图片描述

最后,如果传递的是一个很大的结构体,那用指针传递更优。

5、补充

关于使用指针类型的场景,还有:insert数据时,数据库中默认值不对的时候:

在这里插入图片描述

到此这篇关于go中值传递和指针传递的使用的文章就介绍到这了,更多相关go 值传递和指针传递内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 适合PHP同学的GoFrame框架使用体验及学习建议

    适合PHP同学的GoFrame框架使用体验及学习建议

    这篇文章主要为大家介绍了非常适合PHP同学使用的GoFrame框架设计思想使用体验及学习建议介绍,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-06-06
  • go实现脚本解释器gscript

    go实现脚本解释器gscript

    这篇文章主要为大家介绍了go实现脚本解释器gscript示例代码,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-07-07
  • Go高级特性探究之协程池详解

    Go高级特性探究之协程池详解

    在并发编程中,协程是 Go 语言的核心特性之一,本文将介绍如何使用 Go 协程池构造一个协程池,并解决函数传参问题、优雅关闭协程池和保证协程安全的问题,感兴趣的可以了解一下
    2023-06-06
  • Go语言中的iota关键字的使用

    Go语言中的iota关键字的使用

    这篇文章主要介绍了Go语言中的iota关键字的使用的相关资料,需要的朋友可以参考下
    2023-08-08
  • 完美解决beego 根目录不能访问静态文件的问题

    完美解决beego 根目录不能访问静态文件的问题

    下面小编就为大家带来一篇完美解决beego 根目录不能访问静态文件的问题。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-06-06
  • 使用Golang打印特定的日期时间的操作

    使用Golang打印特定的日期时间的操作

    这篇文章主要给大家详细介绍了如何使用Golang打印特定的日期时间的操作,文中有详细的代码示例,具有一定的参考价值,需要的朋友可以参考下
    2023-07-07
  • Go语言中常用的基础方法总结

    Go语言中常用的基础方法总结

    这篇文章主要为大家详细介绍了Go语言中常用的一些基础方法,例如:使用正则表达式验证字符串、格式化字符串、时间的比较等等,需要的可以参考一下
    2022-09-09
  • goland 清除所有的默认设置操作

    goland 清除所有的默认设置操作

    这篇文章主要介绍了goland 清除所有的默认设置操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-04-04
  • golang 监听服务的信号,实现平滑启动,linux信号说明详解

    golang 监听服务的信号,实现平滑启动,linux信号说明详解

    这篇文章主要介绍了golang 监听服务的信号,实现平滑启动,linux信号说明详解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-05-05
  • GoLand IDE 无法识别 Go工作区中的引用(解决方案)

    GoLand IDE 无法识别 Go工作区中的引用(解决方案)

    GoLand因未识别go.work工作区导致未解析引用错误,需通过正确配置工作区目录或使用replace语句解决,最佳实践是启用GoModulesIntegration,确保IDE与命令行工具一致,本文给大家介绍GoLand IDE无法识别Go工作区中的引用问题,感兴趣的朋友一起看看吧
    2025-09-09

最新评论