美文网首页
Golang参数传递问题

Golang参数传递问题

作者: 普朗tong | 来源:发表于2020-04-18 11:08 被阅读0次

首先说结论:在Go语言里,所有的参数传递都是值传递(传值),都是一个副本,一个拷贝,因为拷贝的内容有时候是非引用类型(int、string、struct等这些),这样就在函数中就无法修改原内容数据;有的是引用类型(指针、map、slice、chan等这些),这样就可以修改原内容数据。

非引用类型(值类型):int,float,bool,string,以及数组和struct
特点:变量直接存储值,内存通常在栈中分配,栈在函数调用完会被释放

引用类型:指针,slice,map,chan,接口,函数等
特点:变量存储的是一个地址,这个地址存储最终的值。内存通常在堆上分配,当没有任何变量引用这个地址时,该地址对应的数据空间就成为一个垃圾,通过GC回收

array

(1)使用值传递在函数间传递大数组

//声明一个需要8 MB的数组
var array [1e6]int
// 将数组传递给函数 foo 
foo(array)
// 函数 foo 接受一个 100 万个整型值的数组 ,实质是创建一个原数组的拷贝
func foo(array [1e6]int) {
     ... 
}

(2)使用指针在函数间传递大数组

//分配一个需要8 MB的数组
var array [1e6]int
// 将数组的地址传递给函数 foo 
foo(&array)
// 函数 foo 接受一个指向 100 万个整型值的数组的指针 ,实质是创建一个指向原数组的指针
func foo(array *[1e6]int) {
       ...
 }

slice

在函数间传递切片 ,也是以值的方式传递,传递的是slice的一个拷贝,但由于slice这个结构体自身尺寸较小,在 64 位架构的机器上,一个切片需要 24 字节的内存:指针字段需要 8 字节,长度和容量字段分别需要 8 字节,因此在函数间复制和传递切片成本很低。我们可以通过切片内部指向底层数据的指针来修改原数据。

 // 分配包含 100 万个整型值的切片
slice := make([]int, 1e6)
// 将 slice 传递到函数 foo slice = foo(slice)
// 函数 foo 接收一个整型切片,并返回这个切片 
func foo(slice []int) []int {
      ...
     return slice
}
函数调用之后两个切片指向同一个底层数组

map

在函数间传递映射时也是值传递,只不过我们在创建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) *hma{
//省略无关代码
}

在map作为参数传递时,使用了这个指针的拷贝进行传递,属于值传递,因此我们可以通过map来修改原有数据,本质上是通过指针来操作。

package main

import "fmt"

func main() {
    persons := make(map[string]int)
    persons["张三"] = 19

    mp := &persons

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

func modify(p map[string]int) {
    fmt.Printf("函数里接收到map的内存地址是:%p\n", &p)
    p["张三"] = 20
}
//输出:
//原始map的内存地址是:0xc00007c010
//函数里接收到map的内存地址是:0xc000084008
//map值被修改了,新值为: map[张三:20]

结构体

结构体本身属于值类型,在函数间作为参数传递时为该结构体的一个拷贝,属于值传递,因此也无法修改原数据,同样的,我们可以通过指针来实现修改原数据的目的

(1)使用值传递在函数间传递结构体

package main

import "fmt"

func main() {
    p:=Person{"张三"}
    fmt.Printf("原始Person的内存地址是:%p\n",&p)
    modify(p)
    fmt.Println(p)
}

type Person struct {
    Name string
}

func modify(p Person) {
    fmt.Printf("函数里接收到Person的内存地址是:%p\n",&p)
    p.Name = "李四"
}
//输出
//原始Person的内存地址是:0xc00008e040
//函数里接收到Person的内存地址是:0xc00008e050
//{张三}

(2)使用指针在函数间传递结构体

package main

import "fmt"

func main() {
    p := Person{"张三"}
    modify(&p)
    fmt.Println(p)
}

type Person struct {
    Name string
}

func modify(p *Person) {
    p.Name = "李四"
}
//输出;{李四}

channel

channel本质上和map一样,可以通过源码看到:

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

相关文章

  • Golang参数传递问题

    首先说结论:在Go语言里,所有的参数传递都是值传递(传值),都是一个副本,一个拷贝,因为拷贝的内容有时候是非引用类...

  • 轻松理解Go函数传参内幕

    一、内置类型作为参数传递 首先要明确一点:golang语言中是没有引用传递的 先上结论:golang的所有内置类型...

  • Golang中参数传递详解

    关于参数传递,Golang文档中有这么一句: after they are evaluated, the para...

  • 关于 Golang 的参数传递

    这里我们着重讨论参数传递的方式以及在 Golang 中函数调用前后(当然包括参数传递)对实参的影响。先了解一些基...

  • 【GOLANG】Go 的参数是如何传递的?

    ### 在 Golang 中所有的类型传递都是通过值传递实现的 #### 举例说明: 1. 普通参数 ``` fu...

  • go如何编写命令行(cli)程序

    创建一个命令行程序 问题 如何使用golang创建可以在命令行当中传递参数的程序?go如何带参数执行程序?比如我们...

  • golang不定长参数问题

    golang不定长参数问题 一般函数的参数都是定长的,但是有一些参数可以传入不定数量的参数,golang 语言也有...

  • golang 方法作为参数传递

    栗子: f1 done f2 done 100 f1 done f2 done xxxx f3 done [[he...

  • golang-101-hacks(12)——切片作为函数参数传递

    注:本文是对golang-101-hacks中文翻译。在Go语言中,函数参数是值传递。使用slice作为函数参数时...

  • Java参数传递问题

    关于Java参数传递问题的一点思考, 主要关注以下两个问题: 1. 参数是如何传递的? 参数通过复制的方式传递。 ...

网友评论

      本文标题:Golang参数传递问题

      本文链接:https://www.haomeiwen.com/subject/anupvhtx.html