Go关键字--func

func

func关键字用来定义函数,函数是golang中非常重要的一块。定义一个函数的语法格式是:

func fnctionName(arg1 dataType,arg2 dataType)(dataType,dataType){
    fmt.Println("func body")
    return "a","b"
}

函数多返回值是golang的特性之一,在声明返回值时,可以创建带变量名的返回值,语法如下:

func functionName(arg1 dataType,arg2 dataType)(retName dataType){
    fmt.Println("func body")
    retName = "hello world"
    return
}

上边的retName就是函数返回值的变量名,在函数的body内,可以直接给retName这个返回值变量赋值,效果等同于return retName

函数调用方法:

如果是同一个包中的函数,直接使用函数名加上括号即可调用,如果函数需要参数,则在括号内传入参数即可。如下代码所示:

package main

import (
    "fmt"
)

func functionName(str string) {
    fmt.Println(str)
}

func main() {
    functionName("hello world")
}

如果要引用另一个包中的函数,首先使用import导入想要引用的包,然后以包名为前缀访问包中可导出函数。上面示例代码中Println函数在fmt包中,在调用Println函数时,首先导入了fmt包,然后通过fmt为前缀,调用Println函数。

函数的几个特点
* 参数
* 返回值

1.函数参数

golang函数支持两种形式的参数,第一是:固定个数参数,第二是可变个数参数。固定个数参数,就是函数接收固定个数的参数,如创建一个2个参数且没有返回值的函数

// 第一种写法
func World(str1, str2 string) {
    fmt.Println(str1, str2)
}

// 第二种写法
func World(str1 string, str2 string) {
    fmt.Println(str1, str2)
}

当参数类型相同时,可以简写成第一种写法。如果参数类型不同,只能采用第二种写法,依次指定每一个参数的类型。

上边介绍了固定参数的函数,下边介绍可变参数函数。可变参数只能是函数的最后一个参数,定义可变参数,只需要将参数的类型前边加上三点(…)即可。定义可变参数语法格式是:

func functionName(arg ...dataType){
    fmt.Println("func body")
}

下边来一段示例代码,详细的介绍函数可变参数情形:

package main

import (
    "fmt"
)

func VarParameter(str1 string, num int, other ...string) {
    fmt.Println("第一个参数:", str1)
    fmt.Println("第二个参数:", num)
    fmt.Println("可变参数:", other, "参数类型是:", reflect.ValueOf(other).Kind())
    for index, val := range other {
        fmt.Println("可变参数第", index, "个值是:", val)
    }
}

func main() {
    VarParameter("var", 100, "a", "b", "c")
}

输出信息:

第一个参数: var
第二个参数: 100
可变参数: [a b c] 参数类型是:slice
可变参数第 0 个值是: a
可变参数第 1 个值是: b
可变参数第 2 个值是: c

other是一个可变参数,那么在golang中,可变参数是一个什么类型呢?答案是:slice。获取可变参数中的值,只需要按照slice类型变量的操作方法即可。
如果可变参数不是函数最后一个参数,那么在编译时会提示如下错误信息:

can only use ... with final parameter in list

如果在创建函数时,可变参数存在多种数据类型,则可以将可变参数类型设置成interface{},示例代码如下:

package main

import (
    "fmt"
    "reflect"
)

func VarParameter(str1 string, num int, other ...interface{}) {
    fmt.Println("第一个参数:", str1)
    fmt.Println("第二个参数:", num)
    fmt.Println("可变参数:", other, "参数类型是:", reflect.ValueOf(other).Kind())
    for index, val := range other {
        fmt.Println("可变参数第", index, "个值是:", val, ",参数类型是:", reflect.ValueOf(val).Kind())
    }
}

func main() {
    VarParameter("var", 100, 1, "b", 3.234)
}

输出信息是:

第一个参数: var
第二个参数: 100
可变参数: [1 b 3.234] 参数类型是: slice
可变参数第 0 个值是: 1 ,参数类型是: int
可变参数第 1 个值是: b ,参数类型是: string
可变参数第 2 个值是: 3.234 ,参数类型是: float64

参数传递时,是值传递,还是指针传递呢?

当变量被当做参数传入调用函数时,是值传递,也称变量的一个拷贝传递。如果传递过来的值是指针,就相当于把变量的地址作为参数传递到函数内,那么在函数内对这个指针所指向的内容进行修改,将会改变这个变量的值。如下边示例代码:

package main

import (
    "fmt"
)

func PtrTest(str *string) {
    *str = "world"
}

func main() {
    var str = "hello"
    PtrTest(&str)
    fmt.Println("str value is:", str)
}

输出信息是:

str value is: world

从上边的输出信息可知,str变量地址当做参数传入函数后,在函数中对地址所指向内容进行了修改,导致了变量str值发生了变化。
这个过程能否说明函数调用传递的是指针,而不是变量的拷贝呢?下边通过另一个例子来进行说明:

package main

import (
    "fmt"
)

var workd = "hello wolrd"

func PtrTest(str *string) {

    str = &workd
}

func main() {
    var str = "hello"
    PtrTest(&str)
    fmt.Println("str value is:", str)
}

输出信息是:

str value is: hello

上边示例中,str变量地址被作为参数传入到了函数PtrTest中,在函数中对参数进行重新赋值,将world变量地址赋值给了参数,函数调用结束后,重新打印变量str值,发现值没有被修改。所以,在函数调用中,变量被拷贝了一份传入函数,函数调用结束后,拷贝的值被丢弃。如果拷贝的是变量的地址,那么在函数内,其实是通过修改这个地址所指向内存中内容,从而达到修改变量值的目的,但是函数内并不能修改这个变量的地址,也就是str变量虽然将地址当做参数传入到PtrTest函数中,PtrTest函数中虽然对这个地址进行了修改,但是在函数调用结束后,拷贝传递进去并被修改的参数被丢弃,str变量地址未发生变化。

2.多返回值

golang中一个函数可以有多个返回值,多返回值函数定义如下:

package main

import (
    "fmt"
)

func functionName(str string) (string, int) {
    return "hello" + str, 200
}

func main() {
    msg, status := functionName("hello world")
    fmt.Println("msg is:", msg)
    fmt.Println("status is :", status)
}

输出信息是:

msg is: hellohello world
status is : 200

golang语言允许给返回值设置变量名,写法如下:

package main

import (
    "fmt"
)

func functionName(str string) (msg string, status int) {
    msg = "hello " + str
    status = 200
    return
}

func main() {
    msg, status := functionName("hello world")
    fmt.Println("msg is:", msg)
    fmt.Println("status is :", status)
}

给返回值设置变量名后,在函数体内,可以直接使用返回值变量,无需在函数体内定义。

函数类型变量

golang可以定义函数类型的变量,函数类型的变量可以被调用。定义函数类型变量示例代码:

package main

import (
    "fmt"
    "reflect"
)

func main() {
    var f = func(str string) {
        fmt.Println("hello", str)
    }
    fmt.Println("类型是:", reflect.ValueOf(f).Kind())
    f("func type")
}

输出信息是:

类型是: func
hello func type

函数类型变量也可以当做参数传递给另一个函数,然后在另一个函数中执行,示例代码如下:

package main

import (
    "fmt"
    "reflect"
)

func exec(f func(str string)) {
    f("func type")
}

func main() {
    var f = func(str string) {
        fmt.Println("hello", str)
    }
    fmt.Println("类型是:", reflect.ValueOf(f).Kind())
    exec(f)
}

输出信息是:

类型是: func
hello func type

exec函数接收一个函数类型的参数,那么是不是只要是函数,就可以被当做参数传入到exec中吗?答案是:不行。 从exec函数的参数列表可知,exec接收一个函数类型参数,这个函数类型参数接收一个字符串类型的参数。

匿名函数与函数闭包

匿名函数,就是定义函数的时候没有给函数取名字。定义函数类型变量,其实就是匿名函数的一种形式。而匿名函数又是闭包的一种形式,闭包示例如下:

package main

import (
    "fmt"
)

func main() {
    var str = "hello"
    var num = 200

    // 闭包,没有函数名也可称为匿名函数
    func() {
        fmt.Println(str, num)
    }()
}

输出信息是:

hello 200

嵌套层级稍微复杂一些的闭包示例如下:

package main

import (
    "fmt"
)

func main() {

    var num = 200
    var p = func() func() {
        var s = 100
        return func() {
            fmt.Println(s + num)
        }
    }()
    p()
    num = 400
    p()
}

输出结果是:

300
500

闭包在程序设计中有着诸多的价值,闭包中可以直接使用外部的变量,闭包内的变量又可以不被闭包外访问,保证闭包内变量的安全性。

猜你喜欢

转载自blog.csdn.net/hzwy23/article/details/79877768
今日推荐