iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > GO >一文搞懂Golang 值传递还是引用传递
  • 308
分享到

一文搞懂Golang 值传递还是引用传递

Golang值传递还是引用传递Golang值传递go引用传递 2023-01-11 15:01:28 308人浏览 泡泡鱼
摘要

目录Go 官方的定义传值和传引用什么是传值(值传递)什么是传引用(引用传递)总结参考资料Go 官方的定义 本部分引用 Go 官方 FAQ 的 “When are func

Go 官方的定义

本部分引用 Go 官方 FAQ 的 “When are function parameters passed by value?”,内容如下。

如同 C 系列的所有语言一样,Go 语言中的所有东西都是以值传递的。也就是说,一个函数总是得到一个被传递的东西的副本,就像有一个赋值语句将值赋给参数一样。

传值和传引用

什么是传值(值传递)

传值的意思是:函数传递的总是原来这个东西的一个副本,一副拷贝。其指的是在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。 比如我们传递一个int类型的参数,传递的其实是这个参数的一个副本;传递一个指针类型的参数,其实传递的是这个该指针的一份拷贝,而不是这个指针指向的值。

对于int这类基础类型我们可以很好的理解,它们就是一个拷贝,但是指针呢?我们觉得可以通过它修改原来的值,怎么会是一个拷贝呢?下面我们看个例子。

test_demo.go

package main

import (
	"fmt"
	"testing"
)

func modify(ip *int) {
	fmt.Printf("函数里接收到的指针的内存地址是:%p\n", &ip)
	*ip = 1
}

func TestDemo(t *testing.T) {
	i := 10
	ip := &i
	fmt.Printf("原始指针的内存地址是:%p\n", &ip)
	modify(ip)
	fmt.Println("int值被修改了,新值为:", i)

}

输出结果:

原始指针的内存地址是:0xc00000e038
函数里接收到的指针的内存地址是:0xc00000e040
int值被修改了,新值为: 1

什么是传引用(引用传递)

传引用,也叫做引用传递, 指在调用函数时将实际参数的地址直接传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。

在 Go 语言中,官方已经明确了没有传引用,也就是没有引用传递这一情况。

争议最大的 map 和 slice
这时候又有小伙伴疑惑了,你看 Go 语言中的 map 和 slice 类型,能直接修改,难道不是同个内存地址,不是引用了?

其实在 FAQ 中有一句提醒很重要:“map 和 slice 的行为类似于指针,它们是包含指向底层 map 或 slice 数据的指针的描述符”。

迷惑map

package main

import (
	"fmt"
	"testing"
)

func modify(p map[string]int) {
	fmt.Printf("函数里接收到map的内存地址是:%p\n", &p)
	p["张三"] = 20
}

func TestDemo(t *testing.T) {
	persons := make(map[string]int)
	persons["张三"] = 19

	mp := &persons

	fmt.Printf("原始map的内存地址是:%p\n", mp)
	modify(persons)
	fmt.Println("map值被修改了,新值为:", persons)
}

输出结果:

原始map的内存地址是:0xc000114028
函数里接收到map的内存地址是:0xc000114030

确实是值传递,那修改后的 map 的结果应该是什么。既然是值传递,那肯定就是 “这次一定!",对吗?

输出结果:

map值被修改了,新值为: map[张三:20]

原因:

指针类型可以修改,非指针类型不行,可以大胆的猜测,使用make函数创建的map是不是一个指针类型呢?看一下源代码:

// makemap implements a Go map creation make(map[k]v, hint)
// If the compiler has determined that the map or the first bucket
// can be created on the stack, h and/or bucket may be non-nil.
// If h != nil, the map can be created directly in h.
// If bucket != nil, bucket can be used as the first bucket.
func makemap(t *maptype, hint int64, h *hmap, bucket unsafe.Pointer) *hmap {
    //省略无关代码
}

通过查看src/runtime/HashMap.go源代码发现,注意其返回的是 *hmap类型,是一个指针。也就是 Go 语言通过对 map 类型的相关方法进行封装,达到了用户需要关注指针传递的作用。

现在看func modify(p map)这样的函数,其实就等于func modify(p *hmap),和前面什么是值传递里举的func modify(ip *int)的例子一样,可以参考分析。

这类情况我们称其为 “引用类型” ,但 “引用类型” 不等同于就是传引用,又或是引用传递了,还是有比较明确的区别的。

chan类型

chan类型本质上和map类型是一样的,这里不做过多的介绍,参考下源代码:

func makechan(t *chantype, size int64) *hchan {
    //省略无关代码
}

chan也是一个引用类型,和map相差无几,make返回的是一个*hchan

和map、chan都不一样的slice

slicemapchan都不太一样的,一样的是,它也是引用类型,它也可以在函数中修改对应的内容。

package main

import (
	"fmt"
	"testing"
)

func modify(ages []int) {
	fmt.Printf("函数里接收到slice的内存地址是%p\n", ages)
	ages[0] = 1
}

func TestDemo(t *testing.T) {
	ages := []int{6, 6, 6}
	fmt.Printf("原始slice的内存地址是%p\n", ages)
	modify(ages)
	fmt.Println(ages)
}

从结果来看,两者的内存地址一样,也成功的变更到了变量 ages 的值。这难道不是引用传递吗?
关注两个细节:

  • 没有用 & 来取地址。
  • 可以直接用 %p 来打印。

之所以可以同时做到上面这两件事,是因为标准库 fmt 针对在这一块做了优化

func (p *pp) fmtPointer(value reflect.Value, verb rune) {
    var u uintptr
    switch value.Kind() {
    case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Slice, reflect.UnsafePointer:
        u = value.Pointer()
    default:
        p.badVerb(verb)
        return
    }
    //省略部分代码
}

通过源代码发现,对于chanmapslice等被当成指针处理,通过value.Pointer()获取对应的值的指针。

// If v's Kind is Slice, the returned pointer is to the first
// element of the slice. If the slice is nil the returned value
// is 0.  If the slice is empty but non-nil the return value is non-zero.
func (v Value) Pointer() uintptr {
    // TODO: deprecate
    k := v.kind()
    switch k {
    //省略无关代码
    case Slice:
        return (*SliceHeader)(v.ptr).Data
    }
}

很明显了,当是slice类型的时候,返回是slice这个结构体里,字段Data第一个元素的地址。

type SliceHeader struct {
    Data uintptr
    Len  int
    Cap  int
}

type slice struct {
    array unsafe.Pointer
    len   int
    cap   int
}

在 Go 语言运行时,传递的也是相应 slice 类型的底层数组的指针,但需要注意,其使用的是指针的副本。严格意义是引用类型,依旧是值传递。slice是一种结构体+元素指针的混合类型,通过元素array(Data)的指针,可以达到修改slice里存储元素的目的。

总结

最终可以确认的是Go语言中所有的传参都是值传递(传值),都是一个副本,一个拷贝。

让最多人犯迷糊的就是 slicemapchan 等类型,都会认为是 “引用传递”,从而认为 Go 语言的 xxx 就是引用传递。正因为它们还引用类型(指针、map、slice、chan等这些),这样就可以修改原内容数据。

再记住,Go里只有传值(值传递)。

参考资料

群里又吵起来了,Go 是传值还是传引用?
Go语言参数传递是传值还是传引用

到此这篇关于golang 值传递还是引用传递的文章就介绍到这了,更多相关Golang: 值传递还是引用传递内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

您可能感兴趣的文档:

--结束END--

本文标题: 一文搞懂Golang 值传递还是引用传递

本文链接: https://www.lsjlt.com/news/177418.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 一文搞懂Golang 值传递还是引用传递
    目录Go 官方的定义传值和传引用什么是传值(值传递)什么是传引用(引用传递)总结参考资料Go 官方的定义 本部分引用 Go 官方 FAQ 的 “When are func...
    99+
    2023-01-11
    Golang值传递还是引用传递 Golang值传递 go引用传递
  • Java是值传递还是引用传递
    本篇内容主要讲解“Java是值传递还是引用传递”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java是值传递还是引用传递”吧!1.值类型通俗意义上来说,所谓的值类型指的就是 Java 中的 8 ...
    99+
    2023-06-16
  • java支持值传递还是引用传递
    本篇内容主要讲解“java支持值传递还是引用传递”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“java支持值传递还是引用传递”吧!文章目的:验证Java语言到底是值传递还是引用传递以及Java参...
    99+
    2023-06-30
  • Java编程是值传递还是引用传递
    这篇文章主要介绍“Java编程是值传递还是引用传递”,在日常操作中,相信很多人在Java编程是值传递还是引用传递问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Java编程是值传递还是引用传递”的疑惑有所帮助!...
    99+
    2023-06-30
  • 浅谈Java到底是值传递还是引用传递呢
    目录一、前言二、值传递与引用传递三、基本数据类型四、对象引用五、结论一、前言 最近在看Java核心卷一,也就是这本书: 在这本书里面也看到了这个问题,Java是值传递还是引用传递,...
    99+
    2022-11-12
  • 深入理解python中函数传递参数是值传递还是引用传递
    目前网络上大部分博客的结论都是这样的: Python不允许程序员选择采用传值还是传 引用。Python参数传递采用的肯定是“传对象引用”的方式。实际上,这种方式相当于传值和传引用的一种综合。如果函数收到的是...
    99+
    2022-06-04
    函数 参数 python
  • 深入探究Java编程是值传递还是引用传递
    目录1.基本数据类型的参数传递2.引用数据类型的参数传递3.原理 文章目的:验证Java语言到底是值传递还是引用传递以及Java参数传递的实现原理. 问题引入: 先阅读代码段: pu...
    99+
    2022-11-13
  • Go语言参数传递是传值还是传引用
    目录什么是传值(值传递)什么是传引用(引用传递)迷惑Mapchan类型和map、chan都不一样的slice小结对于了解一门语言来说,会关心我们在函数调用的时候,参数到底是传的...
    99+
    2022-06-07
    GO 参数 go语言
  • php数组传递是引用传值吗
    PHP数组传递不是引用传递,而是值传递;在调用函数时通过将PHP数组作为实参赋给形参,在函数中修改,并不会影响到数组本身,说明此过程中的传递是值传递,数组变量并非指向此数组本身的引用。php零基础到就业直播视频课:进入学习程序员必备接口测试...
    99+
    2022-09-20
  • 这一次,彻底解决Java的值传递和引用传递
    本文旨在用最通俗的语言讲述最枯燥的基本知识学过Java基础的人都知道:值传递和引用传递是初次接触Java时的一个难点,有时候记得了语法却记不得怎么实际运用,有时候会的了运用却解释不出原理,而且坊间讨论的话题又是充满争议:有的论坛帖子说Jav...
    99+
    2023-06-02
  • php按值传递和引用传递的区别是什么
    这篇文章主要讲解了“php按值传递和引用传递的区别是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“php按值传递和引用传递的区别是什么”吧!说明按值传递中php必须复制值。特别是对于大型...
    99+
    2023-06-20
  • java中值传递和引用传递的区别是什么
    在Java中,值传递(pass by value)和引用传递(pass by reference)是两种不同的参数传递方式。值传递是...
    99+
    2023-08-14
    java
  • 一篇文中告诉你JS中的"值传递"和"引用传递"
    目录前言初步了解堆栈堆栈和类型的关系特点变量赋值参数传递小结面试题两者的区别就是:总结前言 现代的前端开发,不再是刀耕火种的 JQ 时代,而是 MVVM ,组件化,工程化,承载着日益...
    99+
    2022-11-13
  • 解析Golang中引用类型是否进行引用传递
    目录引言引用类型引用变量(reference variable)和引用传递(pass-by-reference)Golang是否存在引用变量(reference variable)字...
    99+
    2022-11-11
  • Python函数值传递、引用传递、形式参数和实际参数的区别是什么
    本篇内容主要讲解“Python函数值传递、引用传递、形式参数和实际参数的区别是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Python函数值传递、引用传递、形式参数和实际参数的区别是什么”...
    99+
    2023-06-30
  • Golang函数的函数传递作为值和引用对比分析
    随着互联网和计算机技术的发展,编程语言的需求逐渐增加。近年来,Golang已经成为了开发人员的首选语言之一。Golang作为一种编译型语言,具有高效和稳定的执行速度,因此被广泛使用于Web应用开发和分布式系统构建。在Golang中,函数的函...
    99+
    2023-05-17
    Golang 函数传递 值和引用
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作