脚本之家,脚本语言编程技术及教程分享平台!
分类导航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|

服务器之家 - 脚本之家 - Golang - Go语言中的函数详解

Go语言中的函数详解

2022-07-13 08:38奋斗的大橙子 Golang

函数是基本的代码块,用于执行一个任务。本文详细讲解了Go语言中的函数,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

1.函数的声明定义

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//func关键字
 
//getStudent函数名
 
//(id int, classId int) 参数列表
 
//(name string,age int) 返回值列表
 
func getStudent(id int, classId int)(name string,age int) {
 
   //函数体
 
  if id==1&&classId==1{
 
     name = "BigOrange"
 
     age = 26
 
   }
 
  //返回值
  
  return name, age // 支持多重返回值
 
}

有意思的是Go语言的返回值可以有多个,并且放在了参数列表后面,而C#等都是在函数名之前,也没有关键字。

2.函数的调用

?
1
2
3
4
5
import "fmt"
 
//调用fmt包中的Println方法。
 
fmt.Println("Name:", std.Name, "Age:",std.Age)

3.函数编写的原则

很好奇为什么没有public private等关键字,那函数怎么才能定义为公用和私有呢?

Go语言有这样的规则:小写字母开头的函数只在本包内可见,大写字母开头的函数才

能被其他包使用。这个规则也适用于类型和变量的可见性。

4.不定参数问题

不定参数是指函数传入的参数个数为不定数量。

?
1
2
3
4
5
func myfunc(args ...int) {
     for _, arg := range args {
     fmt.Println(arg)
  }
}

函数myfunc()接受不定数量的参数,这些参数的类型全部是int

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

它是一个语法糖(syntactic sugar),即这种语法对语言的功能并没有影响,但是更方便程序员使用。

从内部实现机理上来说,类型...type本质上是一个数组切片,也就是[]type,这也是为

什么上面的参数args可以用for循环来获得每个传入的参数。

不定参数的传递

?
1
myfunc3(args ...int)

对应上面的这个函数,传递参数可以为下面两种

?
1
2
3
4
// 按原样传递
myfunc3(args...)
// 传递片段,实际上任意的int slice都可以传进去
myfunc3(args[1:]...)

任意类型的不定参数

可以看到 fmt.Println()方法接受了不定参数,但它是 ...interface{}

Go语言中的函数详解

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

5.多返回值

Go语言函数可以返回多个返回值

如果调用方调用了一个具有多返回值的方法,但是却不想关心其中的某个返回值,可以简单

地用一个下划线“_”来跳过这个返回值

6.匿名函数

Go语言支持随时在代码里定义匿名函数。

匿名函数由一个不带函数名的函数声明和函数体组成

?
1
2
3
func(a, b int, z float64) bool {
     return a*b <int(z)
}

匿名函数可以直接赋值给一个变量或者直接执行:(有点像js哈)

?
1
2
3
f := func(x, y int) int {
     return x + y
}
?
1
2
3
func(ch chan int) {
    ch <- ACK
} (reply_chan) // 花括号后直接跟参数列表表示函数调用

7.闭包

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main
import (
   "fmt"
)
func main() {
   var j int = 5
   a := func()(func()) {
      var i int = 10
      return func() {
         fmt.Printf("i, j: %d, %d\n", i, j)
      }
   }()
   a()
   j *= 2
   a()
}

结果:

i, j: 10, 5
i, j: 10, 10

分析:

1---"...func()(func()) {....."

表明此匿名函数返回值的类型是func(), 即此匿名函数返回一个函数指针(此处引用一下c 的概念);

2---"...returnfunc() {

fmt.Printf("i, j: %d, %d\n", i, j)

}..."

表明返回的函数指针指向一个打印i, j: %d, %d\n的函数;

3---"...a := func()(func()) {

...

}()..."

末尾的括号表明匿名函数被调用,并将返回的函数指针赋给变量a ;

综合来看:

?
1
2
3
4
5
6
7
8
9
10
11
"...a := func()(func()) {
 
var i int = 10
 
return func() {
 
fmt.Printf("i, j: %d, %d\n", i, j)
 
}
 
}()..."

此代码片段的意思"等价于"

?
1
2
3
4
5
a := func() {
 
fmt.Printf("i, j: %d, %d\n", i, j)
 
}

至于为何要用匿名函数如此的转一圈,是因为要引用闭包的概念,此概念省略不表,多写点代码试试就能体会了。

补充:传值和传引用

Go语言中的函数详解

1.type 定义一个类型,有两种方式

  • ①配合struct,创建一个新的结构,类似C#里面的Class
  • ②配合既存的类型(int64...),创建一个新的类型,类似C++里面的typedef

2.Struct的如果不进行赋值,会使用0值进行初始化。

3.type使用既存类型定义新结构,和既存类型不是同一个类型,不能进行转换,例如

?
1
2
3
4
5
6
7
8
package main
 
type Integer int64
 
func main() {
    var srcInt Integer
    srcInt = int64(1000)
}

结果:

Go语言中的函数详解

4.方法和函数的区别

方法能给用户定义的类型添加新的行为。方法实际上也是函数,只是在声明时,在关键字func 和方法名之间增加了一个参数

例如:

这是函数,它的调用就是直接在使用的时候 传入参数获取返回值就行

?
1
2
3
4
func getStudentName(student Student)(name string) {
    //返回值
    return student.Name
}

这是方法

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package main
 
import (
    "fmt"
)
 
type Student struct
{  
    Name string
    Age int
}
 
//Student类的方法 使用值接收者实现了一个方法
func (student Student) getStudentName()(string){
    return student.Name
}
 
//Student类的方法 使用指针接收者实现了一个方法
func (student *Student) changeStudentName(name string){
    student.Name = name
    fmt.Println("方法执行之后name",student.Name)
 
}
 
//Student类的方法 使用指针接收者实现了一个方法
func (student Student) changeStudentNameByValue(name string){
    student.Name = name
    fmt.Println("方法执行之后name",student.Name)
}
 
func main() {
    bigOrange:=Student{
        Name:"BigOrange",Age:18,
    }
 
    bigApple:=Student{
        Name:"BigApple",Age:20,
    }
 
    //使用函数获取学生名称
    name1 := getStudentName(bigOrange)
    name2 := getStudentName(bigApple)
    
    fmt.Println("========通过传地址ChangeName之后==========")
    fmt.Println("方法执行之前name",name1)
    bigOrange.changeStudentName("BigBanana")
    name1 = bigOrange.getStudentName()
    fmt.Println("方法返回之后Name",name1)
 
    fmt.Println("========通过传值ChangeName之后===========")
    fmt.Println("方法执行之前name",name2)
    bigApple.changeStudentNameByValue("BigPear")
    name2 = bigApple.getStudentName()
    fmt.Println("方法返回之后Name",name2)
}

结果:

========通过传地址ChangeName之后==========
方法执行之前name BigOrange
方法执行之后name BigBanana
方法返回之后Name BigBanana
========通过传值ChangeName之后===========
方法执行之前name BigApple
方法执行之后name BigPear
方法返回之后Name BigApple

上面的例子中

分别使用了函数和方法进行获取学生姓名

分别使用了传值和传地址的方式对学生名称进行修正

综上

  • ① Go语言中的方法,相比于函数,多了一个接收者参数
  • ② Go 语言里有两种类型的接收者:值接收者和指针接收者

方法如果使用 值接收者,那么调用者可以是值接收者类型、也可以是它的指针类型,请看下面的例子(补充上例):

?
1
2
3
4
5
6
7
8
fmt.Println("========使用指针来调用值类型声明的接收者方法===========")
bigGrape := &Student{ Name:"bigGrape",Age:22} //取地址!!!!
name3 := bigGrape.getStudentName();
fmt.Println("========通过传值ChangeName之后===========")
fmt.Println("方法执行之前name",name3)
bigGrape.changeStudentNameByValue("BigXXXX")
name3 = bigGrape.getStudentName()
fmt.Println("方法返回之后Name",name3)

结果:

========使用指针来调用值类型声明的接收者方法===========
name bigGrape
========通过传值ChangeName之后===========
方法执行之前name bigGrape
方法执行之后name BigXXXX
方法返回之后Name bigGrape

如上代码 使用了&获取地址,所以bigGrape是一个student类型的指针。下面的代码和上例一样,直接使用了【变量.方法】的方式调用方法,结果也和值类型调用的一样。

为什么?

【Go 在代码背后的执行动作】

?
1
name4:=(*bigGrape).getStudentName()

Go 编译器为了支持这种方法调用背后做的事情。【指针被解引用为值】,这样就符合了值接收者的要求。再强调一次,getStudentName 操作的是一个副本,只不过这次操作的是从bigGrape指针指向的值的副本。

同理还记得上面的,这句代码吗?对bigOrange这个变量修改了名称

?
1
bigOrange.changeStudentName("BigBanana")

bigOrange和bigApple明显是值类型,但是 changeStudentName 接收者是一个指针类型,为什么能调用,也是基于Go代码背后的执行动作,将值类型取了地址。

?
1
(&bigOrange).changeStudentName("BigOrange")

所以对于Go语言调用方法的类型,使用值或者指针都是可以的,不用拘泥于类型。

到此这篇关于Go语言函数的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:https://www.cnblogs.com/dcz2015/p/10096374.html

延伸 · 阅读

精彩推荐
  • GolangGo Println和Printf的区别详解

    Go Println和Printf的区别详解

    这篇文章主要介绍了Go Println和Printf的区别详解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    冷月醉雪11172021-02-27
  • Golang手把手教你Golang的协程池设计

    手把手教你Golang的协程池设计

    在很多公司都在陆续的搭建golang的语言栈,大家有没有想过为什么会出现这种情况?今天咱们就来一步一步的揭开协程池的面纱,如果你没有接触过go的协程...

    程序员小饭3752021-06-08
  • Golanggolang 中signal包的Notify用法说明

    golang 中signal包的Notify用法说明

    这篇文章主要介绍了golang 中signal包的Notify用法说明,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    kph_Hajash6712021-04-20
  • GolangGo语言实现的排列组合问题实例(n个数中取m个)

    Go语言实现的排列组合问题实例(n个数中取m个)

    这篇文章主要介绍了Go语言实现的排列组合问题,结合实例形式分析了Go语言实现排列组合数学运算的原理与具体操作技巧,需要的朋友可以参考下 ...

    books19584592020-05-05
  • Golang基于Golang 高并发问题的解决方案

    基于Golang 高并发问题的解决方案

    这篇文章主要介绍了Golang 高并发问题的解决方案,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    iGoogle.ink11332021-06-24
  • GolangGo 并发实现协程同步的多种解决方法

    Go 并发实现协程同步的多种解决方法

    这篇文章主要介绍了Go 并发——实现协程同步的多种解决方法,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下 ...

    mrr5692020-05-19
  • Golanggolang编程入门之http请求天气实例

    golang编程入门之http请求天气实例

    这篇文章主要介绍了golang编程入门之http请求天气实例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧 ...

    embedsky5512020-05-17
  • Golanggolang中使用sync.Map的方法

    golang中使用sync.Map的方法

    这篇文章主要介绍了golang中使用sync.Map的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下 ...

    IT技术小小鸟9382020-08-21