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. Burp Suite Scanner Module - 扫描模块

    Burp Suite Professional 和Enterprise Version的Scaner功能较丰富. 以Professional版本为例,包含Issue activity, Scan qu ...

  2. Ethical Hacking - Web Penetration Testing(5)

    LOCAL FILE INCLUSION Allows an attacker to read ANY file on the same server. Access files outside ww ...

  3. 一个通用的两级Makefile例子

    目的 进行如项目的顶层目录后,运行make,即可直接编译项目中所有的源文件,并生成最终的可执行文件 实现头文件自动依赖 添加源文件不用修改Makefile,且可以自动编译新文件 顶层目录下添加文件夹, ...

  4. 浅谈Python中的深浅拷贝的区别

    深.浅拷贝总结 深拷贝 拷贝可变数据类型,如列表容器: a = [1, 2, [3, 4]] b = copy.deepcopy(a) a 与 b 所指的列表容器的空间地址不一致,即 id(a) != ...

  5. Elasticsearch源码解析:环境搭建

    在之前学习Redis的过程中,我就是一边通过文档/视频学习,一边阅读源码.我发现两种方法可以相辅相成,互相补充.阅读文档可以帮助我们快速了解某个功能,阅读源码有助于我们更深入的理解这一功能的实现及思想 ...

  6. C#和 JS的闭包

    闭包的概念是内层的函数可以引用包含在它外层的函数的变量,即使外层函数的执行已经终止.但该 变量提供的值并非变量创建时的值,而是在父函数范围内的最终值. C#闭包可理解为跨作用域访问函数内变量,那么如何 ...

  7. tomcat 认证爆破之custom iterator使用

    众所周知,BurpSuite是渗透测试最基本的工具,也可是神器,该神器有非常之多的模块:反正,每次翻看大佬们使用其的骚操作感到惊叹,这次我用其爆破模块的迭代器模式来练练手[不喜勿喷] 借助vulhub ...

  8. 京东阅读(web)体验优化

    京东有电子书可以购买,可以多端阅读.比如PC客户端,移动端,以及本文提到的PC网站端. 先换个镜头,读书要记笔记(电子版本), 方便以后查阅. 镜头换回来,但是,我们为了方便肯定是想复制,下载啊,分享 ...

  9. Python编程入门(第3版)|百度网盘免费下载|零基础入门学习资料

    百度网盘免费下载:Python编程入门(第3版) 提取码:rsd7 目录  · · · · · · 第1章 编程简介 11.1 Python语言 21.2 Python适合用于做什么 31.3 程序员 ...

  10. Python环境那点儿事(MAC篇)

    Python环境那点儿事(MAC篇) 解释器版本选择:(Python是解释型语言,相应的选择的就是解释器) 前言: 不管你是什么原因翻看此篇文章,强行安利一篇< 2018 Python官方年度报 ...