zoukankan      html  css  js  c++  java
  • Go语言入门系列(六)之再探函数

    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语言入门系列」。

    如有错误,还请指正。

  • 相关阅读:
    u盘的超级用法
    文件夹访问被拒绝
    web移动前端的click点透问题
    call()apply()ind()备忘录
    Safari中的new Date()格式化坑
    dataURI V.S. CSS Sprites 移动端
    css3属性之 box-sizing
    多人协作代码--公共库的引用与业务约定
    web前端本地测试方法
    依赖包拼合方法
  • 原文地址:https://www.cnblogs.com/xingrenguanxue/p/13478674.html
Copyright © 2011-2022 走看看