百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术流 > 正文

爱上开源之golang入门至实战第四章函数(Func)(三)

citgpt 2024-07-04 14:56 8 浏览 0 评论

爱上开源之golang入门至实战第四章函数(Func)(三)

4.4.3 按值传递(call by value) 按引用传递(call by reference)

Go 默认使用按值传递来传递参数,也就是传递参数的副本。函数接收参数副本之后,在使用变量的过程中可能会对副本 的值进行更改,但不会影响到原来的变量,比如 Function(arg1) 。 如果你希望函数可以直接修改参数的值,而不是对参数的副本进行操作,你需要将参数的地址(变量名前面添加&符 号,比如 &variable)传递给函数,这就是按引用传递,比如 Function(&arg1) ,此时传递给函数的是一个指针。 如果传递给函数的是一个指针,指针的值(一个地址)会被复制,但指针的值所指向的地址上的值不会被复制;我们 可以通过这个指针的值来修改这个指针所指向的地址上的值。(指针也是变量类型,有自己的地址和值,通常 指针的值指向一个变量的地址。所以,按引用传递也是按值传递。)

爱上开源之golang入门至实战第四章函数(Func)(三)


func exchange(a, b int) {
    var temp int
    fmt.Printf("Before exchanged: a=%d, b=%d\n", a, b)
    temp = b
    b = a
    a = temp
    fmt.Printf("After exchanged: a=%d, b=%d\n", a, b)
}


上方定义了一个exchange的函数,函数的传入参数是a,b两个int类型的数字, 函数交换两个参数的数值,这个函数的运行结果就可以充分地演示出按值传递的效果, 参数的参数都是副本, 所以执行结束以后,原有的a,b两个原值在函数体里都是在对副本进行的操作,函数体执行结束,a,b继续使用原值,所以执行完exchange以后,a,b值不会发生变化。


a, b := 1, 2
fmt.Printf("Before Func: a=%d, b=%d\n", a, b)
exchange(a, b)
fmt.Printf("After Func: a=%d, b=%d\n", a, b)

==== OUTPUT ====
Before Func: a=1, b=2
Before exchanged: a=1, b=2
After exchanged: a=2, b=1
After Func: a=1, b=2


结果输出可以看到,在执行之前a=1, b=2; 当进入函数体,两个原值都是用副本, 所以输出 a=1, b=2; 交换两个副本值,输出a=2, b=1;函数体结束,副本销毁,a,b使用原值,所以输出原值a=1, b=2;

我们可以改造一下exchange的函数体,来看看传入的两个参数的指针地址,是否和原来的一样。


func exchange2(a, b int) {
    var temp int
    fmt.Printf("Before exchanged: a[%p]=%d, b[%p]=%d\n", &a, a, &b, b)
    temp = b
    b = a
    a = temp
    fmt.Printf("After exchanged: a[%p]=%d, b[%p]=%d\n", &a, a, &b, b)
}
a, b := 1, 2
fmt.Printf("Before Func: a[%p]=%d, b[%p]=%d\n", &a, a, &b, b)
exchange2(a, b)
fmt.Printf("After Func: a[%p]=%d, b[%p]=%d\n", &a, a, &b, b)

==== OUTPUT ====
Before Func: a[0xc0002f4e08]=1, b[0xc0002f4e10]=2
Before exchanged: a[0xc0002f4e18]=1, b[0xc0002f4e20]=2
After exchanged: a[0xc0002f4e18]=2, b[0xc0002f4e20]=1
After Func: a[0xc0002f4e08]=1, b[0xc0002f4e10]=2


通过这里的代码测试,我们可以看到在函数调用前,和函数体里,函数体使用的是副本,指针地址不是原地址。 在函数体执行完成以后,变量a,b的地址又重新恢复成以前的指针地址,这就是我们所说的按值传递, 参数传递的是值得副本(拷贝对象)。


如果我们需要在上述的方法,确实一定要实现交换值的目标,我们可以通过传指针或者返回值覆盖原值的方式来实现; 代码如下:


传递指针

func exchange3(a, b *int) {
   var temp int
   fmt.Printf("Before exchanged: a[%p]=%d, b[%p]=%d\n", a, *a, b, *b)
   temp = *b  
   *b = *a  // 不能直接b = a 为什么?
   *a = temp
   fmt.Printf("After exchanged: a[%p]=%d, b[%p]=%d\n", a, *a, b, *b)
}
a, b := 1, 2
fmt.Printf("Before Func: a[%p]=%d, b[%p]=%d\n", &a, a, &b, b)
exchange3(&a, &b)
fmt.Printf("After Func: a[%p]=%d, b[%p]=%d\n", &a, a, &b, b)

==== OUTPUT ====
Before Func: a[0xc0002ece18]=1, b[0xc0002ece20]=2
Before exchanged: a[0xc0002ece18]=1, b[0xc0002ece20]=2
After exchanged: a[0xc0002ece18]=2, b[0xc0002ece20]=1
After Func: a[0xc0002ece18]=2, b[0xc0002ece20]=1

这段代码要细品;

品味1: 如何完成值交换的? // 不能直接b = a 为什么?

品味2:参数都换成了int类型的指针,注意点,指针的地址都是原来的地址?


返回值覆盖原值

func exchange4(a, b int) (int, int) {
   var temp int
   fmt.Printf("Before exchanged: a=%d, b=%d\n", a, b)
   temp = b
   b = a
   a = temp
   fmt.Printf("After exchanged: a=%d, b=%d\n", a, b)
   return a, b
}
a, b := 1, 2
fmt.Printf("Before Func: a=%d, b=%d\n", a, b)
a, b = exchange4(a, b)
fmt.Printf("After Func: a=%d, b=%d\n", a, b)

==== OUTPUT ====
Before Func: a=1, b=2
Before exchanged: a=1, b=2
After exchanged: a=2, b=1
After Func: a=2, b=1



几乎在任何情况下,传递指针(一个32位或者64位的值)的消耗都比传递副本来得少。 调用都是更高效的调用,从基本上来说,至少传递指针的方式,只是一个32位或者64位值得复制,而指针传递的方式,就是要对传递的对象进行值拷贝。基本数据类型差异可能不明显,但是对于结构体,或者是数据量大的数组而言,就有很大的差异了。


在函数调用时,像切片(slice)、字典(map)、接口(interface)、函数(Func)、通道(channel)这样的引用类型都是默认使用引用传递(即使没有显式的指出指针)。


func exchange5(a []int) {
   var temp int
   fmt.Printf("Before exchanged: Array=%v\n", a)
   temp = a[1]
   a[1] = a[0]
   a[0] = temp
   fmt.Printf("After exchanged: Array=%v\n", a)
}
a := make([]int, 0)
a = append(a, 1, 2)

fmt.Printf("Before Func: Array=%v\n", a)
exchange5(a)
fmt.Printf("After Func: Array=%v\n", a)

==== OUTPUT ====
Before Func: Array=[1 2]
Before exchanged: Array=[1 2]
After exchanged: Array=[2 1]
After Func: Array=[2 1]


变量直接存储值,内存通常在栈中分配

变量存储的是一个地址,这个地址对应的空间才能真正存储数据(值); 内存通常在堆上分配,当没有任何变量引用这个地址时,该地址对应的数据空间就成为一个垃圾,由GC来回收

相关推荐

js中arguments详解

一、简介了解arguments这个对象之前先来认识一下javascript的一些功能:其实Javascript并没有重载函数的功能,但是Arguments对象能够模拟重载。Javascrip中每个函数...

firewall-cmd 常用命令

目录firewalldzone说明firewallzone内容说明firewall-cmd常用参数firewall-cmd常用命令常用命令 回到顶部firewalldzone...

epel-release 是什么

EPEL-release(ExtraPackagesforEnterpriseLinux)是一个软件仓库,它为企业级Linux发行版(如CentOS、RHEL等)提供额外的软件包。以下是关于E...

FullGC详解  什么是 JVM 的 GC
FullGC详解 什么是 JVM 的 GC

前言:背景:一、什么是JVM的GC?JVM(JavaVirtualMachine)。JVM是Java程序的虚拟机,是一种实现Java语言的解...

2024-10-26 08:50 citgpt

使用Spire.Doc组件利用模板导出Word文档
  • 使用Spire.Doc组件利用模板导出Word文档
  • 使用Spire.Doc组件利用模板导出Word文档
  • 使用Spire.Doc组件利用模板导出Word文档
  • 使用Spire.Doc组件利用模板导出Word文档
跨域(CrossOrigin)

1.介绍  1)跨域问题:跨域问题是在网络中,当一个网络的运行脚本(通常时JavaScript)试图访问另一个网络的资源时,如果这两个网络的端口、协议和域名不一致时就会出现跨域问题。    通俗讲...

微服务架构和分布式架构的区别

1、含义不同微服务架构:微服务架构风格是一种将一个单一应用程序开发为一组小型服务的方法,每个服务运行在自己的进程中,服务间通信采用轻量级通信机制(通常用HTTP资源API)。这些服务围绕业务能力构建并...

深入理解与应用CSS clip-path 属性
深入理解与应用CSS clip-path 属性

clip-pathclip-path是什么clip-path 是一个CSS属性,允许开发者创建一个剪切区域,从而决定元素的哪些部分可见,哪些部分会被隐...

2024-10-25 11:51 citgpt

HCNP Routing&Switching之OSPF LSA类型(二)
  • HCNP Routing&Switching之OSPF LSA类型(二)
  • HCNP Routing&Switching之OSPF LSA类型(二)
  • HCNP Routing&Switching之OSPF LSA类型(二)
  • HCNP Routing&Switching之OSPF LSA类型(二)
Redis和Memcached的区别详解
  • Redis和Memcached的区别详解
  • Redis和Memcached的区别详解
  • Redis和Memcached的区别详解
  • Redis和Memcached的区别详解
Request.ServerVariables 大全

Request.ServerVariables("Url")返回服务器地址Request.ServerVariables("Path_Info")客户端提供的路...

python操作Kafka

目录一、python操作kafka1.python使用kafka生产者2.python使用kafka消费者3.使用docker中的kafka二、python操作kafka细...

Runtime.getRuntime().exec详解

Runtime.getRuntime().exec详解概述Runtime.getRuntime().exec用于调用外部可执行程序或系统命令,并重定向外部程序的标准输入、标准输出和标准错误到缓冲池。...

promise.all详解 promise.all是干什么的
promise.all详解 promise.all是干什么的

promise.all详解promise.all中所有的请求成功了,走.then(),在.then()中能得到一个数组,数组中是每个请求resolve抛出的结果...

2024-10-24 16:21 citgpt

Content-Length和Transfer-Encoding详解
  • Content-Length和Transfer-Encoding详解
  • Content-Length和Transfer-Encoding详解
  • Content-Length和Transfer-Encoding详解
  • Content-Length和Transfer-Encoding详解

取消回复欢迎 发表评论: