飞道的博客

[Golang]函数详解

237人阅读  评论(0)


函数在其他语言中也是一种很重要的存在。函数其实就是一个能够执行特定任务并且能够重复使用的代码块,可以能够减少代码的重复率。

函数定义

在GO中,函数的定义可能与其他语言相比,有些不同之处:

func 函数名称 (参数列表) (返回值列表){
函数体 }

func : Go中的关键字,用来定义一个函数
函数名称也是一种标识符,符合之前的规则:只能包含字母,数字,下划线;并且不能够以数字开头;倘若首字母大写,则可以在别的包中使用,倘若首字母小写,只能在本包当中进行使用;
参数列表: 形参变量名称 数据类型的形式进行声明,多个形参之间需要使用逗号隔开 返回值列表:
与其他语言一样,可以只写返回值的数据类型,多个返回值用逗号隔开 函数体:实现特定功能的代码块

在Go中,支持返回值命名即返回值列表也可以支持 变量 数据类型的形式;而且支持返回多个返回值。此外,如果函数没有返回值,则返回值列表可以进行省略。

eg:实现一个函数,能够计算两数之和和两数之差

func Add_Del(num1 int,num2 int) (sum int,diff int){
   
      sum= num1 + num2
      diff = num1- num2
      return
}
//可以有多个返回值,此处为两个int类型的返回值,解决了C语言为了有多个返回值而使用指针或引用变量作为出参的尴尬境地
//支持返回值命名,就如同变量一样进行使用

注: 如果我们对函数的返回值进行了命名,其实在函数开始的时候,就会用该数据类型的默认值进行初始化。
在同一个包当中,是不能够出现同名函数的,故Go中式不支持函数重载这个机制的

可变参数

该语法支持传入任何数目的参数。
语法: 在变量后面加入 三个点("…") , 表示此处可以传入可变参数
当使用可变参数的时候,需要注意:
一个函数最多只能够有一个可变参数变量
如果函数参数列表中除可变参数外还有参数,则可变参数一定要写在参数列表的最右侧


注: 传参
当要传入若干个参数到有可变参数的函数当中,可以传入多个变量,也可以将多个变量放在slice中,进行传入

匿名函数

在Go中,函数可以作为值,可以将其赋值给一个变量,可以通过这个变量,去调用函数。

匿名函数是没有函数名称的函数,Go语言支持这种机制,如果一个函数仅仅调用一次,我们可以使用这个机制

//定义一个匿名函数
func (num1,num2 int)(sum int){
   
    sum = num1 + num2
    return 
}
//如果我们在定义完毕匿名函数之后,立即进行调用,就要用到如下的形式(使用小括号,传入函数的参数)
func (num1,num2 int)(sum int){
   
    sum = num1 + num2
    return 
}(10,7)

如果想要多次调用匿名函数,可以考虑将匿名函数赋值给一个变量,通过变量可以多次调用该函数。

此外,匿名函数还主要用来实现闭包。

闭包

闭包是由函数和与其相关的引用环境组合而成的实体。概念可以简化为 闭包 = 函数 + 相关引用环境

闭包其实类似于一个函数,但是它由相关的引用用环境,所以说具有所谓的“记忆化”的功能,我们可以参考下面的代码来理解:

  6 func AddUpper() func(int)int{
   
  7     var num int = 0;
  8     fmt.Println("num = ",num)
  9     return func(x int)int{
   
 10         num = num + x
 11         fmt.Println("line : 11 , num = ", num )
 12         return num;
 13     }
 14 
 15 }

 19 func main(){
   
 20    fun := AddUpper();
 21    fmt.Println(fun(1));
 22    fmt.Println(fun(2));
 23    fmt.Println(fun(3));
 24 
 25 }

展示结果:

num =  0
line : 11 , num =  1
1
line : 11 , num =  3
3
line : 11 , num =  6
6

init函数

每个源文件中都可以包含一个init函数,init函数是在main函数运行之前,被Go运行框架所调用
测试:

func init() {
   
	fmt.Println("init()")

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

}

结果:

init()
main()

如果存在全局变量的话,那么调用顺序是怎么样的呢?

func fun() int {
   
	fmt.Println("fun()")
	return 777
}
//全局变量p
var p int = fun()
//init函数
func init() {
   
	fmt.Println("init()")

}
//main函数
func main() {
   
	fmt.Println("main()")

}

结果:

fun()
init()
main()

先初始化全局变量,调用init函数,调用main函数

如果引用的包中包含init函数那么调用顺序是怎么样的呢?

存在一个main.go文件,引包的顺序为 ll包 cal包 ,ll包和cal包全有全局变量,init函数,main.go文件中有全局变量,init函数,main函数,他们的调用顺序为图标号所示

程序验证: (程序见附录)

ll/全局变量
ll/init()
cal/cal.go/全局变量
cal/cal.go/init()函数
main/全局变量
main/init()
main()

存在一个main.go文件,引包的顺序为cal包 ll包 ,ll包和cal包全有全局变量,init函数,main.go文件中有全局变量,init函数,main函数,他们的调用顺序为图标号所示

程序验证:

cal/cal.go/全局变量
cal/cal.go/init()函数
ll/全局变量
ll/init()
main/全局变量
main/init()
main()

存在一个main.go文件,引 ll包 ,存在ll.go文件,引cal包 ,ll包和cal包全有全局变量,init函数,main.go文件中有全局变量,init函数,main函数,他们的调用顺序为图标号所示


程序验证

cal/cal.go/全局变量
cal/cal.go/init()函数
ll/全局变量
ll/init()
main/全局变量
main/init()
main()

作用

init函数在main函数执行之前就被调用,因此我们可以用其来初始化我们的执行环境,其调用顺序如上图所示

附录:

//main.go
package main

import (
	"fmt"
        "test04/test5"
        "test04/cal"
)

func fun() int {
   
	fmt.Println("main/全局变量")
	return 0
}

var p int = fun()

func init() {
   
	fmt.Println("main/init()")
}

func main() {
   
	fmt.Println("main()")
	cal.Sum(7)
        fmt.Println(ll.P)
}

//cal.go
package cal

import (
	"fmt"
)

func fun() int {
   
	fmt.Println("cal/cal.go/全局变量")
	return 0
}
var p int = fun()
func init() {
   
	fmt.Println("cal/cal.go/init()函数")
}
func Sum(n int) {
   
	sum := 0
	for i := 1; i <= n; i++ {
   
		sum += i
	}
	fmt.Printf("从1到 %d 的和为 %d\n", n, sum)
}

//ll.go
package ll

import (
	"fmt"
)

func fun() int {
   
	fmt.Println("ll/全局变量")
	return 0
}

var P int = fun()

func init() {
   
	fmt.Println("ll/init()")
}


转载:https://blog.csdn.net/weixin_43519514/article/details/117034291
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场