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 - GAINING ACCESS(11)

    CLIENT SIDE ATTACKS - Listening for connections 1. Run Metasploit Move the backdoor file to the webs ...

  2. 修改虚拟机中的centos系统分辨率

    使用vmware虚拟机安装centos系统,默认分辨都很低,可使用以下方法修改虚拟机中centos系统的分辨率 1,# vi /boot/grub/grub.conf 2,找到 kernel 的那一行 ...

  3. C++语法小记---同名覆盖

    同名覆盖 子类中的同名成员会覆盖父类中的同名成员,但是在内存中仍然存在,只是无法直接访问,需要加上域名才能访问 子类中的同名函数会覆盖父类中的函数,复写是同名覆盖的一种特殊情况,只要不是多态场景,复写 ...

  4. day12:闭包函数&匿名函数

    闭包函数 闭包函数的定义: 如果内函数使用了外函数的局部变量并且外函数把内函数返回出来的过程 叫做闭包里面的内函数是闭包函数 一个简单的闭包函数示例: def songyunjie_family(): ...

  5. 附001.Nginx location语法规则

    一 location规则 1.1 location语法 基本语法: location [=|~|~*|^~]/uri/{...} 修饰符释义: 1 = #表示精确严格匹配,只有请求的url路径与后面的 ...

  6. Python学习周期 学习Python要多久?

    学习python编程需要多长时间?首先我们需要明确一点,在互联网技术领域,技术始终在不断的迭代升级,只要进入IT行业就要时刻保持学习的状态,才能不被技术进步的车轮碾压.我们目前讨论的python学习周 ...

  7. Android Studio采坑记录

    折腾了几个月的Android Studio,终于在今天被我搞定了 ( ̄▽ ̄)~* 开贴记录下,免得下次再次采坑 先说下我之前电脑的环境配置吧,sdk是几年前在网上下载别人整理出来的包,一直没有更新过 ...

  8. DJANGO-天天生鲜项目从0到1-002-用户模块-注册

    本项目基于B站UP主‘神奇的老黄’的教学视频‘天天生鲜Django项目’,视频讲的非常好,推荐新手观看学习 https://www.bilibili.com/video/BV1vt41147K8?p= ...

  9. jmeter配置原件之使用CSV Data Set Config参数化

    测试过程中经常需要对发送的请求进行参数化,jmeter提供的CSV Data Set Config 配置元件可以很好的对请求数据进行参数化,下面介绍使用CSV Data Set Config参数化 1 ...

  10. html标签集合

    ---恢复内容开始--- 基础 <!DOCTYPE>,<html>,<title>,<body>,<h1>to<h6>,< ...