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. Ethical Hacking - Web Penetration Testing(11)

    SQL INJECTION Preventing SQLi Filters can be bypassed. Use a blacklist of commands? Still can be byp ...

  2. Python Hacking Tools - Password Sniffing

    Password Sniffing with Scapy 1. Download and install the Scapy first. pip install scapy https://scap ...

  3. 项目管理:如何显性管理并提升Story分解能力

    引言: 在“DevOps能力之屋(CapabilitiesHouse of DevOps)”中,华为云DevCloud提出(工程方法+最佳实践+生态)×工具平台=DevOps能力.华为云DevClou ...

  4. 高效C++:继承和实现

    如何正确的使用继承和实现是本章说明的重点. 确定public继承的关系是is-a public继承等同于is-a 对public继承,所有base的特性,在derived上都适用 避免遮掩继承而来的名 ...

  5. python-study-文件操作

    # 一.文件操作的作用 :读取内容.写入内容.备份内容.... # 文件的基本操作,文件操作包含:打开.关闭.读.写.复制.... # 打开 读写 关闭 # 文件备份 # 文件和文件夹的操作 # 总结 ...

  6. 一个文本框的各项说明,包括类似html的table

    https://www.cnblogs.com/zhqiux/archive/2013/09/03/3298654.html

  7. Laragon修改配置快速创建其他框架的项目

    配置方式 依葫芦画瓢,如添加thinkPHP: # Thinkphp Thinkphp 3.2=composer create-project topthink/thinkphp %s Thinkph ...

  8. matplotlib图表介绍

    Matplotlib 是一个python 的绘图库,主要用于生成2D图表. 常用到的是matplotlib中的pyplot,导入方式import matplotlib.pyplot as plt 一. ...

  9. Microsoft Cloud App Security 微软的云应用安全

    1.概述 微软2015年收购的一家云安全创业公司 Adallom 正式推出产品,同时更名为微软 Cloud App Security.Adallom 成立于 2012年,是一家 SaaS 云安全公司, ...

  10. zabbix 监控nginx状态

    1. 安装nginx,参考其它 [root@iZ2zeapnvuohe8p14289u6Z /]# vim /etc/nginx/nginx.conf # 加上下面 location /nginx_s ...