编辑
2020-02-04
编程语言
00
请注意,本文编写于 1328 天前,最后修改于 105 天前,其中某些信息可能已经过时。

目录

函数定义
可变参数
返回值
函数类型
匿名函数和闭包
延迟调用 defer
获取命令行参数
作用域

本文主要记录了Golang的函数定义,可变参数,函数返回值,函数类型匿名函数和闭包, 延迟调用,变量的作用域以及如何获取命令行参数,Golang是可以有多个返回值的,这一点能干很多事情,函数类型的主要功能在于回调和多态的实现,就像函数指针一样,用起来也很方便!

函数定义

go
func FuncName(/* 参数列表 */)(o1 type, o2 type /* 返回值类型 */ ){ return v1, v2; }

函数定义说明:

  • func: 函数由关键字func开始声明
  • FumcName: 函数名称,根据约定,函数名首字母小写即为private,大写即为public
  • 参数列表: 函数可以有0个或多个参数,参数格式为:变量名类型,如果有多个参数通过逗号分隔,不支持默认参数
  • 返回类型: ①上面返回值声明了两个变量名o1和o2(命名返回参数),这个不是必须,可以只有类型没有变量名 ②如果只有一个返回值且不声明返回值变量,那么你可以省略,包括返回值的括号 ③如果没有返回值, 那么就直接省略最后的返回信息 ④如果有返回值, 那么 必须在函数的内部添加retun语句
go
package main import "fmt" //无参无返回值函数定义 func MyFunc(){ fmt.Println("MyFunc") } //带参无返回值 func MyFunc3(a int){ a = 999 fmt.Println(a) } //带多个参数无返回值 func MyFunc4(a int, b int) { fmt.Printf("a=%d, b=%d\n", a, b) } //参数一样时可以简写 func MyFunc5(a, b int) { fmt.Printf("a=%d, b=%d\n", a, b) } //参数一样时可以简写,a、b都是int、e是float64、d、f都是string func MyFunc6(a, b int, e float64, d, f string) { fmt.Printf("a=%d, b=%d\n", a, b) } func main() { MyFunc() MyFunc2() MyFunc3(666) MyFunc4(666, 777) MyFunc5(666, 777) } //无参无返回值函数定义,定义在main之后也OK func MyFunc2(){ fmt.Println("MyFunc2") }

可变参数

和C语言一样,可变参数放最后,可传可不传

可变参数的参数传递方式,参考MyFunc04,如果是只传一部分,参考MyFunc05、MyFunc06

go
package main import "fmt" //固定参数 func MyFunc01(a, b int) { } //可变参数 func MyFunc02(args ... int) { for i, data := range args { fmt.Printf("args[%d]=%d\n", i, data) } } //固定参数+可变参数 //固定参数必传、可变参数可以为空 //可变参数一定要放在最后 func MyFunc03(a int, b string, args ... int) { fmt.Println("a =", a) fmt.Println("b =", b) for i, data := range args{ fmt.Printf("args[%d]=%d\n", i, data) } } //可变参数的传递 func MyFunc04(args ... int) { MyFunc02(args ...) } //可变参数的传递 //特殊情况:比如只想把后面两个参数传递给另外一个函数使用 func MyFunc05(args ... int) { //从args[2]开始(包括本身),把后面所有元素传过去 MyFunc02(args[2:] ...) } func MyFunc06(args ... int) { //从args[2]开始(不包括本身),把前面所有元素传过去 MyFunc02(args[:2] ...) } func main() { //MyFunc02(11, 22, 33, 44) // OK //MyFunc02() // OK //MyFunc03(11, "hello", 22, 33) MyFunc04(11, 22, 33, 44) fmt.Printf("-----------------------------\n") MyFunc05(11, 22, 33, 44) fmt.Printf("-----------------------------\n") MyFunc06(11, 22, 33, 44) }

返回值

go
package main import "fmt" //无参有一个返回值 func MyFunc01() int { return 100 } //给返回值起一个变量名(golang的推荐写法) func MyFunc02() (result int) { return 200 } //给返回值起一个变量名(golang的推荐写法) func MyFunc03() (result int) { result = 300 return } func main() { a := MyFunc01() fmt.Printf("a=%d\n", a) b := MyFunc02() fmt.Printf("b=%d\n", b) c := MyFunc03() fmt.Printf("c=%d\n", c) }

多个返回值的情况

go
package main import "fmt" //多个返回值 func MyFunc01() (int, int, int) { return 11, 22, 33 } //官方推荐写法 func MyFunc02() (a int, b int, c int) { a, b, c = 11, 22, 33 return } func main() { _, _, c := MyFunc01() fmt.Printf("c=%d\n", c) _, b, _ := MyFunc02() fmt.Printf("b=%d\n", b) }

多参数多个返回值

go
package main import "fmt" func MaxValue(a, b int) (max, min int) { if a > b { max = a min = b }else{ min = a max = b } return } func main() { max, _ := MaxValue(11, 22) fmt.Printf("max=%d\n", max) }

递归的例子

go
package main import "fmt" func MyFunc01(num int) (ret int) { if num == 1 { return 1 }else { return num + MyFunc01(num - 1) } } func main() { num := MyFunc01(100) fmt.Printf("num=%d\n", num) }

函数类型

函数也是一种数据类型,通过type可以定义它,它的类型就是所有拥有的相同的参数,相同的返回值的一种类型

go
package main import "fmt" func Add(a, b int) (ret int) { return a + b } func Sub(a, b int) (ret int) { return a - b } //声明一个函数类型 type FuncType func(int, int) int //多态:多种形态,调用一个接口不同的表现形态,这里相当于把函数类型当作参数传递了 func MyCalc(calc FuncType, a, b int) int { return calc(a, b) } func main() { //传统使用方式 ret := Add(10, 20) fmt.Printf("ret=%d\n", ret) //函数类型调用方式 var myFunc FuncType myFunc = Add ret = myFunc(20, 30) //等价于Add(20, 30) fmt.Printf("ret=%d\n", ret) fmt.Printf("Add ret=%d\n", MyCalc(Add, 10, 5)) //15 fmt.Printf("Sub ret=%d\n", MyCalc(Sub, 10, 5)) //5 }

函数类型的应用:回调函数,也就是通过这种传递函数参数的方式实现的

其实底层实现就是函数指针数组

匿名函数和闭包

所谓闭包就是一个函数“捕获”了和它在同一作用域的其它常量和变量。这就意味着当闭包被调用的时候,不管在程序什么地方调用,闭包能够使用这些常量或者变量。它不关心这些捕获了的变量和常量是否已经超出了作用域,所以只有闭包还在使用它,这些变量就还会存在。

在Go语言里,所有的匿名函数 (Go语言规范中称之为函数字面量) 都是闭包。匿名函数是指不需要定义函数名的一种函数实现方式,它并不是一个新概念,最早可以回溯到1958年的Lisp语言

go
package main import ( "fmt" ) func main() { //匿名函数,无函数名 myFunc01 := func() { fmt.Printf("hello\n") } myFunc01() //给一个函数类型起别名 type FuncType func() //无参数无返回值 var myFunc02 FuncType = myFunc01 myFunc02() //定义匿名函数并调用 func() { fmt.Printf("hello\n") }() //()代表调用此匿名函数 //带参数的匿名函数 myFunc03 := func(a, b int) { fmt.Printf("a=%d, b=%d\n", a, b) } myFunc03(10, 20) //定义带参数的匿名函数并调用 func(a, b int) { fmt.Printf("a=%d, b=%d\n", a, b) }(10, 20) //定义匿名函数带参带返回值 x, y := func(i, j int)(max, min int){ if i > j { max, min = i, j }else { max, min = j, i } return }(10, 20) fmt.Printf("x=%d, y=%d\n", x, y) }

闭包以引用方式捕获外部变量

go
package main import "fmt" func main() { a := 10 s := "hello" func(){ //闭包以引用方式捕获外部变量 a = 666 s = "world" //内部:a=666, s=world fmt.Printf("内部:a=%d, s=%s\n", a, s) }() //内部:a=666, s=world fmt.Printf("内部:a=%d, s=%s\n", a, s) }

闭包的特点

go
package main import "fmt" //函数的返回值是一个匿名函数,返回一个函数类型 func test02() func()int { var x = 0 return func() int { x++ return x * x } } func main() { //函数的返回值是一个匿名函数,返回一个函数类型 //通过f来调用返回的匿名函数,即调用闭包 f := test02() fmt.Printf("%d\n", f()) // 1 fmt.Printf("%d\n", f()) // 4 fmt.Printf("%d\n", f()) // 9 fmt.Printf("%d\n", f()) // 12 } func test01() int { //函数调用的时候,x才分配空间 var x = 0 x++ return x * x //调用完毕 x释放 } func main01() { fmt.Printf("%d\n", test01()) // 1 fmt.Printf("%d\n", test01()) // 1 fmt.Printf("%d\n", test01()) // 1 fmt.Printf("%d\n", test01()) // 1 }

mark

所以可以看出变量的声明周期不是由它的作用域决定的,如果有闭包在使用它,那么它的声明周期是由闭包是否还在继续调用决定的!

延迟调用 defer

关键字defer用于延迟一个函数或者方法(或者当前所创建的匿名函数)的执行,注意:defer语句只能出现在函数或者方法的内部, 文件操作时比较常用

mark

下面看看多个defer的执行顺序

如果一个函数中有多个defer语句,它们会以LIFO (后进先出)的顺序执行。哪怕函数或某个延迟调用发生错误,这些调用依旧会被执行。

mark

defer和匿名函数结合使用

mark

与上面的例子做一个对比

mark

所以很明显可以看出来,defer就是延迟调用,但是不代表延时传参数

获取命令行参数

mark

作用域

定义在{ } 里面的变量就是局部变量,注意if或者for语句里定义的变量也是属于if或者for语句独有的,执行到变量那句话的时候才分配内存空间,离开作用域内存自动释放

go
package main import "fmt" //定义全局变量 var num = 20 //定义全局变量不能使用类型推导 //num := 30 error func main() { fmt.Println("num =", num) }

不同作用域允许定义同名变量,使用变量的原则,就近原则

本文作者:Tim

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!