[Go语言入门] 11 Go语言函数

11 Go语言函数

11.1 函数声明和调用

函数声明

为完成某一功能的程序指令(语句)的集合,称为函数。

Go 语言标准库提供了多种可动用的内置的函数。例如,len() 函数可以接受不同类型参数并返回该类型的长度。


函数声明的语法:

func function_name ([parameter_list]) [return_list] {
    函数体
}

语法说明:

  • func - 函数定义由func关键字开始
  • function_name - 函数的名称
  • parameter_list - 参数列表,参数列表用来指定参数类型、顺序、及参数个数。当函数被调用时,你可以将值传递给参数,这个值被称为实际参数。函数名和参数列表一起构成了函数签名。参数列表是可选的。
  • return_list - 返回值列表,用来指定函数返回值及其数据类型。Go语言函数可以有多个返回值。有些函数不需要返回值,不需要有return_list。
  • 函数体 - 函数定义的代码集合。

示例(函数定义):

/* 函数返回两个数的最大值 */
func max(num1, num2 int) int {
   /* 声明局部变量 */
   var result int

   if (num1 > num2) {
      result = num1
   } else {
      result = num2
   }
   return result
}

函数调用

通过函数调用来执行函数体中的代码。

调用函数,向函数传递参数值,函数执行后,返回值。

示例(调用函数max):

package main

import "fmt"

func main() {
   var a int = 100
   var b int = 200
   var ret int

   /* 调用函数并返回最大值 */
   ret = max(a, b)

   fmt.Printf( "最大值是 : %d\n", ret )
}

/* 函数返回两个数的最大值 */
func max(num1, num2 int) int {
   /* 定义局部变量 */
   var result int

   if (num1 > num2) {
      result = num1
   } else {
      result = num2
   }
   return result
}

内建函数

Go语言预定义了一些函数,无需引用任何包就可以使用他们,下面写出了所有的内建函数:

  • close 用于channel通讯。使用它来关闭channel。
  • delete 用于在map中删除实例。
  • len和cap 可用于不同的类型的值,len用于返回字符串、切片和数组的长度。cap用于返回切片的容量。
  • new 用于各种类型的内存分配。
  • make 用于内建类型(map、slice、channel)的内存分配。
  • copy 用于复制切片
  • append 用于追加切片。
  • panic和recover 用于异常处理机制。
  • print和println 是底层打印函数,可以在不引入fmt包的情况下使用。他们主要用于调试。
  • complex、real和imag 全部用于处理复数。

11.2 函数参数

不定参函数

不定参是指函数的形参个数为不定数量,为了做到这一点,首先需要将函数定义为接受不定数量参数:

package main

import "fmt"

func dynamicParameter(args ...int) int {
  sum := 0
  for _, arg := range args {
    sum += arg
  }
  return sum
}

func main() {
  fmt.Println(dynamicParameter(1, 2, 3, 4))
  fmt.Println(dynamicParameter(1, 2, 3, 4, 5))
}

形如...type格式的数据类型只能作为函数的参数类型存在,而且必须是最后一个参数。

它是一个语法糖。Go编译器会把传入的实际参数打包为一个数组切片传给该形参。数组切片将在后续的课程中介绍。


任意类型的不定参数

前面的例子中将不定参的类型指定为int,如果你希望传任意类型,可以指定类型为interface{}。下面是Go语言标准库中fmt.Printf()的函数原型:

func Printf(format stirng, args ...interface{}) {
  // ...
}

用interface{}传递任意类型数据是Go语言的管理用法。

示例(MyPrintf):

package main

import "fmt"

func MyPrintf(args ...interface{}) {
  for _, arg := range args {
    switch arg.(type) {
    case int:
      fmt.Println(arg, "is an int value.")
    case string:
      fmt.Println(arg, "is a string value.")
    case int64:
      fmt.Println(arg, "is an int64 value.")
    default:
      fmt.Println(arg, "is an unknown type.")
    }
  }
}

func main() {
  var v1 int = 1
  var v2 int64 = 234
  var v3 string = "hello"
  var v4 float32 = 1.234
  MyPrintf(v1, v2, v3, v4)
}

11.3 函数返回值

多个返回值

Go的函数可以返回多个值,例如:

package main

import "fmt"

func swap(x, y string) (string, string) {
   return y, x
}

func main() {
   a, b := swap("Hello", "World")
   fmt.Println(a, b)		// 输出 World Hello
}

对返回值命名

Go函数的返回值支持命名,命名以后的返回值相当于函数内的局部变量,而且Go语言会使用零值对他们进行初始化。

func cal(n1 int, n2 int) (sum int, sub int) {
    sum = n1 + n2
    sub = n1 - n2
    return // 函数会返回sum和sub
}

11.4 传参方式

定义函数时如果使用参数,那么参数列表中的变量称作形参。

形参就像定义在函数体内部的局部变量。

调用函数时,可以通过两种方式来传递参数:

传递类型描述
值传递值传递是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
引用传递引用传递是指在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。

值传递

传递是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。

默认情况下,Go 语言使用的是值传递,即在调用过程中不会影响到实际参数。

以下定义了swap()函数:

/* 定义相互交换值的函数 */
func swap(x, y int) int {
   var temp int

   temp = x /* 保存 x 的值 */
   x = y    /* 将 y 值赋给 x */
   y = temp /* 将 temp 值赋给 y*/

   return temp;
}

接下来,让我们使用值传递来调用swap()函数:

package main

import "fmt"

func main() {
   /* 定义局部变量 */
   var a int = 100
   var b int = 200

   fmt.Printf("交换前 a 的值为 : %d\n", a )
   fmt.Printf("交换前 b 的值为 : %d\n", b )

   /* 通过调用函数来交换值 */
   swap(a, b)

   fmt.Printf("交换后 a 的值 : %d\n", a )
   fmt.Printf("交换后 b 的值 : %d\n", b )
}

/* 定义相互交换值的函数 */
func swap(x, y int) int {
   var temp int

   temp = x /* 保存 x 的值 */
   x = y    /* 将 y 值赋给 x */
   y = temp /* 将 temp 值赋给 y*/

   return temp;
}

执行结果为:

交换前 a 的值为 : 100
交换前 b 的值为 : 200
交换后 a 的值 : 100
交换后 b 的值 : 200

程序中使用的是值传递, 所以两个值并没有实现交互,我们可以使用后面介绍的引用传递来实现交换效果。


指针传递

指针传递是指在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。

以下是交换函数 swap() ,使用了指针传递:

/* 定义交换值函数*/
func swap(x *int, y *int) {
   var temp int
   temp = *x    /* 保持 x 地址上的值 */
   *x = *y      /* 将 y 值赋给 x */
   *y = temp    /* 将 temp 值赋给 y */
}

以下我们使用指针传递来调用 swap() 函数:

package main

import "fmt"

func main() {
   /* 定义局部变量 */
   var a int = 100
   var b int= 200

   fmt.Printf("交换前,a 的值 : %d\n", a )
   fmt.Printf("交换前,b 的值 : %d\n", b )

   /* 调用 swap() 函数
   * &a 指向 a 指针,a 变量的地址
   * &b 指向 b 指针,b 变量的地址
   */
   swap(&a, &b)

   fmt.Printf("交换后,a 的值 : %d\n", a )
   fmt.Printf("交换后,b 的值 : %d\n", b )
}

func swap(x *int, y *int) {
   var temp int
   temp = *x    /* 保存 x 地址上的值 */
   *x = *y      /* 将 y 值赋给 x */
   *y = temp    /* 将 temp 值赋给 y */
}

以上代码执行结果为:

交换前,a 的值 : 100
交换前,b 的值 : 200
交换后,a 的值 : 200
交换后,b 的值 : 100

11.5 匿名函数

匿名函数

定义函数时没有指定函数名。有两种形式:

  1. 在定义匿名函数的时候就直接调用:

    func main() {
        res := func(n1 int, n2 int) int {
            return n1 + n2
        }(10, 20)
        fmt.Println(res)
    }
    
  2. 将匿名函数赋值给某个变量,再通过变量名来调用该函数:

    func main() {
        a := func(n1 int, n2 int) int {
            return n1 - n2
        }
        
        res := a(10, 30)
    }
    

闭包

闭包的概念:闭包就是一个函数和与该函数相关的引用环境组合而成的一个实体。

Go语言的匿名函数可用来实现闭包:当匿名函数中引用了该函数外部所定义的变量就成为了闭包。

以下实例中,我们创建了函数 getSequence() ,返回另外一个函数。该函数的目的是在闭包中递增 i 变量,代码如下:

package main

import "fmt"

func getSequence() func() int {
   i:=0
   return func() int {
      i+=1
     return i  
   }
}

func main(){
   /* nextNumber 为一个函数,函数 i 为 0 */
   nextNumber := getSequence()  

   /* 调用 nextNumber 函数,i 变量自增 1 并返回 */
   fmt.Println(nextNumber())
   fmt.Println(nextNumber())
   fmt.Println(nextNumber())
   
   /* 创建新的函数 nextNumber1,并查看结果 */
   nextNumber1 := getSequence()  
   fmt.Println(nextNumber1())
   fmt.Println(nextNumber1())
}

以上代码执行结果为:

1
2
3
1
2

示例2:

package main

import "fmt"

func getSequence() (func() int, func() int) {
	i := 0
	return func() int {
			i += 1
			return i
		}, func() int {
			i += 2
			return i
		}
}

func main() {
	nextNumber1, nextNumber2 := getSequence()

	fmt.Println(nextNumber1())
	fmt.Println(nextNumber2())
	fmt.Println(nextNumber1())
	fmt.Println(nextNumber2())
	fmt.Println(nextNumber1())
	fmt.Println(nextNumber2())
}

// 执行结果
// 1
// 3
// 4
// 6
// 7
// 9

11.6 延迟函数调用

defer语句

Go语言通过defer语句向函数添加结束时执行的代码,常用于释放某些已分配的资源、关闭数据库连接、断开socket、解锁一个加锁的资源等。Go语言保证函数结束后一定会执行defer语句中的代码。

defer语句在函数return语句之后、返回调用者函数之前执行。下面是一个示例:


package main

import "fmt"

func main() {
    myFunction()
    fmt.Println("after myFunction")
}

func myFunction() {
    fmt.Println("enter myFuntion")
    defer func() {
        fmt.Println("in defer")
    }()
    
    fmt.Println("myFunction return")
    return
}

// 以上程序输出
// enter myFuntion
// myFunction return
// in defer
// after myFunction

多个defer语句的执行顺序

当出现多条defer语句时,按照他们添加的顺序逆序执行(后进先出模式),示例:


func main() {
    myFunction()
}


func myFunction() {
    for i:=0; i < 5; i++ {
        defer fmt.Printf("%d ", i)
    }
}

// 以上程序输出
// 4 3 2 1 0

Copyright@2022 , 359152155@qq.com

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

时空旅客er

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值