Go语言入门系列前面的文章:

Go语言入门系列(二)之基础语法总结这篇文章中已经介绍过了Go语言的函数的基本使用,包括声明、参数、返回值。本文再详细介绍一下函数的其他使用。

1. 变参

Go语言的函数除了支持0个或多个参数,还支持不定数量的参数,即变参。声明方式为:

func foo(变参名 ...参数类型) 函数类型 {
//函数体
}

下面是一个具体的函数,它接收不定数量的int参数,并返回和:

package main

import "fmt"

func add(arg ...int) int { //变参函数
var sum int
for _, value := range arg {
sum += value
}
return sum
} func main() {
sum := add(1, 2, 3, 4)
fmt.Println(sum) //10
}

arg ...int表明add函数接收不定数量的参数,且只能是int类型的。arg是我们给该变参取的名字,它实际上是一个切片,所以在add函数中可以使用range遍历变量arg

2. 传值和传指针

当我们调用一个有参函数时,肯定会向该函数中传入参数:

package main

import "fmt"

//传入一个值,打印它
func printX(x int) {
fmt.Println(x)
} func main() {
var a int = 5
printX(a) //向函数中传入参数:变量a
}

这里有一个问题:我们真的是把变量a传给了printX函数吗?我们用两个例子来说明问题。

2.1. 例1

package main

import "fmt"

func plusOne(x int) int {
x = x + 1
fmt.Println("执行加一")
return x
} func main() {
a := 5
fmt.Println("a =", a) //应该为5 实际为5
b := plusOne(a)
fmt.Println("a =", a) //应该为6 实际为5
fmt.Println("b =", b) //应该为6 实际为6
}

plusOne函数的作用是把传进来的参数加一,并返回结果。

a=5传进plusOne函数,执行了x = x + 1语句,那么执行过后a的值应该为6,但实际为5。

变量b接收了函数的返回值,所以为6,这没问题。

这证明了,我们的a变量根本就没传进函数中,那么实际传的是什么?实际传的是a变量的一份拷贝。

所以,我们向Go语言中的函数传入一个值,实际上传的是该值的拷贝,而非该值本身。

那如果我们确实要把上例中的变量a传入plusOne函数中呢?那此时就不应该传值了,而是应该传入指针。代码改进如下:

package main

import "fmt"

func plusOne(x *int) int { //参数是指针变量
*x = *x + 1
fmt.Println("执行加一")
return *x
} func main() {
a := 5
fmt.Println("a =", a) //应该为5 实际为5
b := plusOne(&a) //传入地址
fmt.Println("a =", a) //应该为6 实际为6
fmt.Println("b =", b) //应该为6 实际为6
}

a=5传进plusOne函数,执行了x = x + 1语句,执行过后a的值实际为6。

这就证明,变量a确实被传进plusOne函数并被修改了。因为我们传进去的是一个指针,即变量的地址,有了地址我们可以直接操作变量。

如果你对指针的使用不熟悉,这里的代码可能会有点难理解,下面逐行解释:

func plusOne(x *int) int {

声明x是一个int类型的指针参数,只接受int类型变量的地址 。

*x = *x + 1

使用*操作符根据x中存的地址,获取到对应的值,然后加一。

return *x

使用*操作符根据x中存的地址,获取到对应的值,然后返回。

b := plusOne(&a)

plusOne函数只接受int类型变量的地址,所以使用&操作符获取a变量的地址,然后才传入。

2.2. 例2

下面我再举一个经典的例子:写一个函数,能够交换两个变量的值。

如果你不知道什么是传值和传指针,那可能会写成这样:

package main

import "fmt"

func swap(x, y int) {
tmp := x
x = y
y = tmp
fmt.Println("函数中:x =", x, ", y =", y)
} func main() {
x, y := 2, 8
fmt.Println("交换前:x =", x, ", y =", y)
swap(x, y)
fmt.Println("交换后:x =", x, ", y =", y)
}

运行结果:

交换前:x = 2 , y = 8
函数中:x = 8 , y = 2
交换后:x = 2 , y = 8

只在函数中完成了交换,出了函数又变回原样了。

想要完成交换,就必须传入指针,而非值拷贝:

package main

import "fmt"

func swap(x, y *int) {
tmp := *x
*x = *y
*y = tmp
fmt.Println("函数中:x =", *x, ", y =", *y)
} func main() {
x, y := 2, 8
fmt.Println("交换前:x =", x, ", y =", y)
swap(&x, &y)
fmt.Println("交换后:x =", x, ", y =", y)
}

运行结果:

交换前:x = 2 , y = 8
函数中:x = 8 , y = 2
交换后:x = 8 , y = 2

传入指针能够真正交换两个变量的值。

传入指针的好处:

  1. 传入指针使我们能够在函数中直接操作变量,多个函数也能操作同一个变量。
  2. 不需要再拷贝一遍值了。如果你需要传入比较大的结构体,再拷贝一遍就多花费系统开销了,而传入指针则小的多。

3. 函数作为值

在Go语言中,函数也可以作为值来传递。下面是一个例子:

package main

import "fmt"

type calculate func(int, int) int // 声明了一个函数类型

func sum(x, y int) int {
return x + y
} func product(x, y int) int {
return x * y
} func choose(a, b int, f calculate) int { //函数作为参数
return f(a, b)
} func main(){
diff := func(x, y int) int { //函数作为值赋给diff
return x - y
} fmt.Println(choose(2, 3, sum)) //5
fmt.Println(choose(4, 5, product)) //20
fmt.Println(choose(6, 7, diff)) //-1
fmt.Println(diff(9, 8)) //1
}

函数作为值或者参数肯定要有对应的类型,类型是:func(参数类型)返回值类型 。比如func(int,int) int

可以使用type关键字给func(int,int) int起个别名叫calculate,方便使用。

choose函数中声明了一个类型为calculate的函数参数f,而我们又编写了calculate类型的函数sumproduct,所以可以向choose函数中传入这两个函数。

我们给变量diff赋了一个函数,所以能够使用diff(9, 8),或者将其作为参数传入choose函数。

作者简介

我是「行小观」,于千万人中的一个普通人。阴差阳错地走上了编程这条路,既然走上了这条路,那么我会尽可能远地走下去。

我会在公众号『行人观学』中持续更新「Java」、「Go」、「数据结构和算法」、「计算机基础」等相关文章。

欢迎关注,我们一起踏上行程。

本文章属于系列文章「Go语言入门系列」。

如有错误,还请指正。

Go语言入门系列(六)之再探函数的更多相关文章

  1. 【Go语言入门系列】(七)如何使用Go的方法?

    [Go语言入门系列]前面的文章: [Go语言入门系列](四)之map的使用 [Go语言入门系列](五)之指针和结构体的使用 [Go语言入门系列](六)之再探函数 本文介绍Go语言的方法的使用. 1. ...

  2. 【Go语言入门系列】(八)Go语言是不是面向对象语言?

    [Go语言入门系列]前面的文章: [Go语言入门系列](五)指针和结构体的使用 [Go语言入门系列](六)再探函数 [Go语言入门系列](七)如何使用Go的方法? 1. Go是面向对象的语言吗? 在[ ...

  3. 【Go语言入门系列】(九)写这些就是为了搞懂怎么用接口

    [Go语言入门系列]前面的文章: [Go语言入门系列](六)再探函数 [Go语言入门系列](七)如何使用Go的方法? [Go语言入门系列](八)Go语言是不是面向对象语言? 1. 引入例子 如果你使用 ...

  4. Go语言入门系列(四)之map的使用

    本系列前面的文章: Go语言入门系列(一)之Go的安装和使用 Go语言入门系列(二)之基础语法总结 Go语言入门系列(三)之数组和切片 1. 声明 map是一种映射,可以将键(key)映射到值(val ...

  5. 【Go语言入门系列】Go语言工作目录介绍及命令工具的使用

    [Go语言入门系列]前面的文章: [保姆级教程]手把手教你进行Go语言环境安装及相关VSCode配置 [Go语言入门系列](八)Go语言是不是面向对象语言? [Go语言入门系列](九)写这些就是为了搞 ...

  6. R语言数据分析系列六

    R语言数据分析系列六 -- by comaple.zhang 上一节讲了R语言作图,本节来讲讲当你拿到一个数据集的时候怎样下手分析,数据分析的第一步.探索性数据分析. 统计量,即统计学里面关注的数据集 ...

  7. Go语言入门系列(五)之指针和结构体的使用

    Go语言入门系列前面的文章: Go语言入门系列(二)之基础语法总结 Go语言入门系列(三)之数组和切片 Go语言入门系列(四)之map的使用 1. 指针 如果你使用过C或C++,那你肯定对指针这个概念 ...

  8. Excel VBA入门(六)过程和函数

    前面讲过,VBA代码有两种组织形式,一种就是过程(前面的示例中都在使用),另一种就是函数.其实过程和函数有很多相同之处,除了使用的关键字不同之外,还有不同的是: 函数有返回值,过程没有 函数可以在Ex ...

  9. C 语言入门---第六章 C语言数组

    数组就是一些列具有相同类型的数据的集合,这些数据在内存中一次挨着存放,彼此之间没有缝隙. 我们可以将二维数组看作一个Excel表格,有行有列,length1 表示行数,length2 表示列数,要在二 ...

随机推荐

  1. Python3 生成器解析

    第6章 函数 6.1 函数的定义和调用 6.2 参数传递 6.3 函数返回值 6.4 变量作用域 6.5 匿名函数(lambda) 6.6 递归函数 6.7 迭代器 6.8 生成器 6.9 装饰器 6 ...

  2. two types of friend

    两类朋友 第一类,普通朋友,并不能分享一些隐私的感情,只能说一些事情,有一些只是认识的人或者虽然认识很多年但是也只能是这样的! 第二类,关心你,可以交流感清,明显更加亲密一点. 不要对第一类朋友说第二 ...

  3. GEDIT外部工具

    首先通过编辑-首选项-插件-外部命令来打开外部命令,然后在工具-Manage External Tools来添加新工具,工具代码使用bash语言. 代码使用方式:+添加新插件,在编辑框中粘贴代码,快捷 ...

  4. mysql 联合唯一

    CREATE TABLE `NewTable` ( `id` int NOT NULL , `name` varchar(255) NULL , `phone` varchar(255) NULL , ...

  5. jmeter之断言、数据提取器(正则表达式、jsonpath、beanshell)、聚合报告、参数化

    ctx - ( JMeterContext) - gives access to the context vars - ( JMeterVariables) - gives read/write ac ...

  6. paramiko上传文件到Linux

    一.传输单个文件到Linux服务器 import paramiko transport = paramiko.Transport(('host',22)) transport.connect(user ...

  7. 在CentOS 7 上为docker配置端口转发以兼容firewall

    在CentOS 7上当我们以类似下列命令将主机端口与容器端口映射时可能遇到无法访问容器服务的问题 docker run --name web_a -p 192.168.1.250:803:80 -d ...

  8. gerrit安装指南

    Gerrit的基本介绍 Gerrit 是一个Git服务器,它基于 git 版本控制系统,使用网页界面来进行审阅工作.Gerrit 旨在提供一个轻量级框架,用于在代码入库之前对每个提交进行审阅,更改将上 ...

  9. 自述:转职IT ,痛苦一阵子;不转职IT,痛苦一辈子(第一章)

    作为一个从后期制作转职过来的Java工程师,我认为我是幸运的,虽然我的本科专业(影视后期)也是火爆行业,不愁工作,但我不后悔进入这个IT坑,毕竟转行,只痛苦一阵子,但是不转行,可能我会痛苦一辈子. 我 ...

  10. python基础day4_列表list

    list列表 li = ['alex',[1,2,3],'hjh','nvshen '] l1= li[0] print(l1) # alex l3= li[0:3]#['alex', [1, 2, ...