go 学习 ,三:函数 & 指针 & 结构体

一、函数

函数声明

// 声明语法:Go 中函数传递的参数都是值传递(将形参的值拷贝一份给函数内部使用, 形参与实参是相互独立互不影响的),其包括普通函数、lambda函数(匿名函数)、方法
 func 函数名(参数) 返回值 {
   函数体
 }

// eg: 斐波那契数列(递归)
func fibonacci(n int) int {
    if n < 2 {
        return n    
    } else {
        return fibonacci(n-2) + fibonacci(n-1)
    }
}

函数返回值

// 一个返回值的函数
func max(num1 int, num2 int) int {
   if num1 > num2 {
      return num1
   } else {
      return  num2
   }
}



// 多个返回值的函数
func allValue(num int, name string) (int, string) {
    fmt.Println("In allValue function num: ", num)
    fmt.Println("In allValue function name: " + name)
    return num, name
}


func main() {
    num1 := 10
    num2 := 22
    result := max(num1, num2)
    fmt.Println("result: ", result)

    num, name := allValue(num2, "power")
    fmt.Println("In main function num: ", num)
    fmt.Println("In main function name: ", name)

}

函数参数

  • 值传递是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
func valueFunc(num1 int, num2 int) {
    num1, num2 = num2, num1
}


// 当参数中多个参数类型一致时, 可简写参数的类型
func f1(x, y int, m, n string, i, j bool) int { 
    return x + y
}


func main() {
    fmt.Println("值传递交换前 num1 的值: ", num1)
    fmt.Println("值传递交换前 num2 的值: ", num2)
    valueFunc(num1, num2)
    fmt.Println("值传递交换后 num1 的值: ", num1)
    fmt.Println("值传递交换后 num2 的值: ", num2)
}


// [Output]:值传递交换前 num1 的值:  20
// [Output]:值传递交换前 num2 的值:  10
// [Output]:值传递交换后 num1 的值:  20
// [Output]:值传递交换后 num2 的值:  10
  • 引用传递是指在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。
func pointFunc(prt1 *int, prt2 *int) {
    *prt1, *prt2 = *prt2, *prt1
}


func main(){    
    num1, num2  := 10, 20
    var prt1 *int = &num1
    var prt2 *int = &num2

    fmt.Println("交换前 num1 的值: ", num1)
    fmt.Println("交换前 num2 的值: ", num2)
    pointFunc(prt1, prt2)                                // 指针作为函数参数,传递的是参数的地址
    fmt.Println("交换后 num1 的值: ", num1)
    fmt.Println("交换后 num2 的值: ", num2)
}        


// [Output]: 交换前 num1 的值:  10
// [Output]: 交换前 num2 的值:  20
// [Output]: 交换后 num1 的值:  20
// [Output]: 交换后 num2 的值:  10
  • 默认情况下,Go 语言使用的是值传递,即在调用过程中不会影响到实际参数。

可变参数:必须放在函数参数的最后, Go 语言中没有默认参数概念

// 接收相同类型的可变参数的函数
func argsSameFunc(args ... string) {
    fmt.Print("same type elem: ")
    for _, param := range args {
        fmt.Print(param, ", ")
    }
    fmt.Println()
}

// 接受不同类型的可变参数的函数
func argsDifferenceFunc(args ... interface{}) {
    fmt.Print("difference type elem: ")
    for _, param := range args {
        fmt.Print(param, ",")
    }
    fmt.Print("\n")
}

// 接收可变数量的参数, 类型约束为字符串
func joinStrings(slist ...string) string {
   // 定义一个字节缓冲, 快速地连接字符串
   var b bytes.Buffer
   // 遍历可变参数列表slist, 类型为[]string
   for _, s := range slist {
      // 将遍历出的字符串连续写入字节数组
      b.WriteString(s)
   }
   // 将连接好的字节数组转换为字符串并输出
   return b.String()
}


func main() {

    // 接受相同类型的可变参数函数
    argsSameFunc("power")
    argsSameFunc("forever", "topOne")
    argsSameFunc("selina", "hebe", "ella")

    // 接受不同类型的可变参数函数
    argsDifferenceFunc("power", 220)
    argsDifferenceFunc("selina", 1031, "hebe", 330, "ella", 618)

  fmt.Println(joinStrings("Asia ", "sky", " crops:", "S.H.E"))
  fmt.Println(joinStrings("They ", "gave ", "me ", "the ", "courage "))
  fmt.Print(reflect.TypeOf(num1))    // 可获取变量的类型
}

函数作为参数、返回值

// 函数作为参数传递
func calc(x, y int, op func(int, int) int) int { // 接收两个int类型的参数, 和 一个拥有 2个int类型参数、1个int类型返回值的函数类型 参数
    return op(x, y)
}

// 函数作为返回值 func do(s string) (func(int, int) int, error) { switch s { case "+": return add, nil case "-": return sub, nil default: err := errors.New("无法识别的操作符") return nil, err } } func add(x, y int) int { return x + y } func sub(x, y int) int { return x - y }

全局、局部变量

1、全局变量:定义在函数外部的变量, 在程序的整个运行周期内都有效, 在函数中可访问到全局变量。
2、局部变量: 分为两种, 第一种 函数内定义的变量无法在该函数外使用; 如果局部变量和全局变量重名,优先访问局部变量
3、局部变量: 第二种 语句块内定义的变量只能在语句块中生效(if、for、switch)

闭包

// 闭包: 闭包是一个函数 它包含这个函数 和 它外部作用域的变量(与其相关的引用环境) 组合而成的实体, 闭包=函数+引用环境; 底层原理: 函数可作为返回值; 函数内部查找变量的顺序, 先在函数内部找, 再往函数外部找

// 无参闭包
func adder() func(int) int { // 返回值的函数声明 与 return后的匿名函数 是一样的
    var x int
    return func(y int) int {
        x += y
        return x
    }
}

// 带参闭包
func adder2(x int) func(int) int {
    return func(y int) int {
        x += y
        return x
    }
}

// 参数为函数引用的闭包
func ft(x, y int) {
    fmt.Println("\nThis is ft function.")
    fmt.Printf("ft(x - %v, y - %v)", x, y)
}

func adders(f func(int, int), x, y int) func() {
    tmp := func() {
        f(x, y)
    }
    return tmp
}
func main() { // 1、无参闭包 var f = adder() // 此时接收到的是 adder 函数返回的 匿名函数的引用 fmt.Println("closesure f(10) result: ", f(10)) // 此前并未给闭包内的x赋值, 所以x声明后为默认的零值, 此行代码运行过后, x已被赋值, x=0, y=10, x+=y, x=10 fmt.Println("closesure f(20) result: ", f(20)) // 在程序并未结束前, 前一句代码给闭包内的x所赋予的值都有效, 所以此行结果为: x=10, y=20, x+=y, x=30 var f1 = adder() // 重新赋值, 从头再来 fmt.Println("closesure f1(30) result: ", f1(30)) // x=0, y=30, x+=y, x=30 fmt.Println("closesure f1(50) result: ", f1(50)) // x=30, y=50, x+=y, x=80 fmt.Println() // 2、带参闭包 var fp = adder2(10) // x=10 fmt.Println("closesure fp(10) result: ", fp(10)) // x=10, y=10, x+=y, x=20 fmt.Println("closesure fp(30) result: ", fp(30)) // x=20, y=30, x+=y, x=50 var fp1 = adder2(20) fmt.Println("closesure fp1(50) result: ", fp1(50)) // x=20, y=50, x+=y, x=70 fmt.Println("closesure fp1(20) result: ", fp1(20)) // x=70, y=20, x+=y, x=90 // 3、参数为函数引用的闭包 var ft1 = adders(ft, 2, 3) ft1() }

匿名函数

// 匿名函数: 没有函数名的函数, 它无法像普通函数那样调用, 需要将匿名函数保存到某个变量, 或作为立即执行的函数 [匿名函数多用于回调函数和闭包]
// 定义语法
func(参数列表)(返回参数列表){
    // 函数体
}

// 匿名函数 -- 作为执行函数(只执行一次), 函数定义完之后 加上() 将会立即执行    func(a, b int) {
    fmt.Println("self excuting function a*b result: ", a*b)
}(2, 3)


// 匿名函数 -- 作为回调函数
func visit(list []int, f func(int)) {
    for _, elem := range list {
        f(elem)
    }
}

func main() {
    // 匿名函数
    // 声明时调用
    func(name string) {
        fmt.Println("Hello ", name)
    } ("power")        // ---> 括号内是匿名函数执行传递的参数

    // 将匿名函数赋值给变量,通过 变量()  调用
    innominateFunc := func(name string) string {
        fmt.Println("Hello ", name)
        receiving := "Hello" + name
        return receiving
    }
    innominateFunc("hebe")

    // 匿名函数作为回调函数        打印切片中的所有元素
    visit([]int {313, 618, 1031}, func(v int) {            // params: 切片,匿名函数(注意,此处的匿名函数在 visit函数的参数括号内,是作为参数传递给 visit 的)
        fmt.Println(v)
    })
}

方法

// 结构体
type Circle struct {
    radius float64
}

// 方法
func (circle Circle) method(msg string) float64 {
    fmt.Println("The radius of the circle is: ", circle.radius)
    return 3.14 * circle.radius * circle.radius
}


func main() {    
    var circle Circle
    circle.radius = 10.00
    area := circle.method("This is a way of finding the area of a circle")
    fmt.Println("The area of the circle is: ", area)

    fmt.Print("\n")    
?

二、指针

package main

import "fmt"


const count int = 5


func pointFunc(prt1 *int, prt2 *int) {
    *prt1, *prt2 = *prt2, *prt1
}



func main() {

    // 指针声明
    var ip *int
    var fp *float32
    var ipNum int
    var fpFloat float32

    ipNum = 618
    fpFloat = 3.13
    // 指针赋值
    ip = &ipNum
    fp = &fpFloat

    fmt.Println("ip: ", ip)
    fmt.Println("fp: ", fp)

    // 空指针
    var prt *string
    fmt.Println("prt: ", prt)
    if (prt != nil){
        fmt.Println("prt isn't empty pointer.")
    } else {
        fmt.Println("prt is empty pointer.")
    }

    fmt.Print("\n")

    // 指针数组
    var array = [] int{1, 3, 5, 7, 9}
    var ptr [count]*int
    zero := 0
    for t:=0; t<count; t++ {
        ptr[t] = &array[t]
    }
    for r:=0; r<count; r++ {
        // 打印数组内元素内存地址赋值给指针数据后的所有内存地址
        fmt.Print(array[r], "\t")
        fmt.Print(*ptr[r], "\t")
        fmt.Print(ptr[r], "\t")

        // 修改指针数组内所有元素的内存地址
        ptr[r] = &zero
        fmt.Print(ptr[r], "\t")
        fmt.Println(*ptr[r])
    }


    fmt.Print("\n")

    // 指向指针的指针变量
    var a = 22
    var aptr1 *int
    var aptr2 **int
    aptr1 = &a
    aptr2 = &aptr1

    fmt.Println("a value: ", a)
    fmt.Println("a memory address: ", &a)
    fmt.Println("aptr1 value: ", aptr1)
    fmt.Println("*aptr1 value: ", *aptr1)
    fmt.Println("&aptr1 memory address: ", &aptr1)
    fmt.Println("aptr2 value: ", aptr2)
    fmt.Println("*aptr2 value: ", *aptr2)
    fmt.Println("**aptr2 value: ", **aptr2)
    fmt.Println("&aptr2 memory address: ", &aptr2)

    fmt.Print("\n")

    // 指针作为函数的参数
    num1, num2  := 10, 20
    var prt1 *int = &num1
    var prt2 *int = &num2

    fmt.Println("指针传递交换前 num1 的值: ", num1)
    fmt.Println("指针传递交换前 num2 的值: ", num2)
    pointFunc(prt1, prt2)
    fmt.Println("指针传递交换后 num1 的值: ", num1)
    fmt.Println("指针传递交换后 num2 的值: ", num2)


}

三、结构体

// 声明语法
type 结构体名称 struct {
        字段名1  字段1类型
        字段名2  字段2类型
        字段名3  字段3类型
         ...
}    


// 示例1:每行声明一个字段
type Member struct {
    name string
    age int
    weight float64
    height float32
}


// 示例2:同类型字段声明在同一行
type Test struct {
    one, two, three int
    four string
    five float32
    six float64
    seven bool
}


// 第一种方式实例化结构体
var members Member
members.name = "power"
members.age = 20
members.weight = 47

// 第二种方式实例化结构体
member := Member{"top", 18, 45, 1.65}
member := Member{"name": "top", "age": 18, "weight": 45, "height": 1.65}
// 打印结构体的每个字段值 func printMemberPoint(memberPoint *Member) { fmt.Println("memberPoint: ", memberPoint) fmt.Println("memberPoint.name: ", memberPoint.name) fmt.Println("memberPoint.age: ", memberPoint.age) fmt.Println("memberPoint.weight: ", memberPoint.weight) fmt.Println("memberPoint.height: ", memberPoint.height) fmt.Print("\n") }
// 第一种方式实例化指针类型的结构体:new pointMember := new(Member) memberPoint.name = "power" memberPoint.age = 20 memberPoint.weight = 47 memberPoint.height = 1.73 printMemberPoint(memberPoint)
// 第二种方式实例化指针类型的结构体:取址 & memberPoint := &Member{} memberPoint.name = "top" memberPoint.age = 18 memberPoint.weight = 45 memberPoint.height = 1.68 printMemberPoint(memberPoint) var memberPoint *Member memberPoint = &members printMemberPoint(memberPoint) // 匿名结构体 stone := struct { nickname string skill string }{"small stone", "fly"} fmt.Println("stone.nickname:", stone.nickname) fmt.Println("stone.skill:", stone.skill)
// 结构体的方法 func (structObject *structName) method(param paramType...) (returnType...) {
// do something
}
returnValue := member.method(param)