Go(二)函数

自定义类型

两种用户定义的类型

  • 结构类型
  • 基于一个已有的类型,将其作为新类型的类型说明

结构体变量定义和初始化

type struct_variable_type struct {
   member definition
   member definition
   ...
   member definition
}

一旦定义了结构体类型,它就能用于变量的声明,语法格式如下:

variable_name := structure_variable_type {value1, value2...valuen}
//
variable_name := structure_variable_type { key1: value1, key2: value2..., keyn: valuen}

实例

package main

import "fmt"

type Person struct {
    name string
    sex byte
    age int
}

func main() {

    // 顺序初始化,必须全部初始化完整
    var person Person = Person{"wang", 'f', 29}
    fmt.Println(person)

    // 部分初始化
    person1 := Person{name: "zhao", sex: 'm'}
    fmt.Println(person1.name)
}

赋值、比较和传参

使用.索引成员变量

    // 部分初始化
    person1 := Person{name: "zhao", sex: 'm'}
    fmt.Println(person1.name)

    person1.name = "li"
    fmt.Println(person1.name)

比较

只能使用 == 和 !=,不能使用 > < >= <=

    // 结构体比较
    person2 := Person{name: "zhao", sex: 'm', age: 18}
    person3 := Person{name: "zhao", sex: 'm', age: 18}
    person4 := Person{name: "zhao", sex: 'm', age: 181}
    fmt.Println(person2 == person3) // true
    fmt.Println(person3 == person4) // false

相同类型的结构体可以进行赋值

相同类型结构体:成员变量的类型、个数、顺序一致

var tmp Person
tmp = person1
fmt.Println(tmp.name)

 因此函数体内部可以使用结构体传参,因为实参可以赋值给相同结构体类型的形参,值传递。几乎不用,内存消耗大,效率低。

func test(person Person)  {
    println(person.name)
}

func main() {

    var temp Person
    test(temp)


}

结构体指针

结构体指针变量定义和初始化

1.顺序初始化和部分初始化

    var man *Person  = &Person{"zhao", 'm', 18}
    fmt.Println(man.name)
    var man1 *Person  = &Person{name: "zhao", sex: 'm'}
    fmt.Println(man1.name)

2.使用new

var  man2 *Person = new(Person);
man2.name = "zhao"
man2.sex = 0
man2.age = 16

使用.指针索引成员变量

结构体变量的地址就是结构体首个元素的地址

    fmt.Printf("&tmp = %p 
", &tmp)
    fmt.Printf("&tmp.name = %p 
", &tmp.name)

结果

&tmp = 0xc000004500
&tmp.name = 0xc000004500

结构体指针的值就是结构体首个元素的地址

fmt.Printf("man2 = %p
", man2)
fmt.Printf("&man2.name = %p
", &man2.name)

结果

man2 = 0xc00008e080
&man2.name = 0xc00008e080

结构体指针作为函数参数

import "fmt"

type Person struct {
    name string
    sex byte
    age int
}

func test1(person * Person)  {
    person.name = "wang"
}

func main() {

    fmt.Println("man2:", man2)
    test1(man2)
    fmt.Println("man2", man2)

}

结果

man2: &{zhao 0 16}
man2 &{wang 0 16}

发现通过指针变量可以修改结构体,传引用,使用频率高

也是实参拷贝值给形参,不过这次拷贝的是地址值

 或

fmt.Println(person1.name)
test1(&person1)
fmt.Println(person1.name)

结果

li
wang

函数是一等公民

与其他主要编程语言的差异

1.可以有多个返回值

2.所有参数都是值传递

       slice、map、channel会有传引用是错觉,如切片背后是数组,是一个数据结构,里面包含了指向对应数组的指针,数据结构被复制,指针操作的仍是同一块空间,感觉像是传引用

3.函数可以作为变量的值

4.函数可以作为参数和返回值

package fun_test

import (
    "fmt"
    "math/rand"
    "testing"
    "time"
)

// 多个返回值
func returnMultiValues()(int, int)  {
    return rand.Intn(10), rand.Intn(20)
}

// 函数可以作为参数和返回值
// 计算inner函数运行的时间
func timeSpent(inner func(op int)int) func(opt int) int  {
    return func(n int) int {
        start := time.Now()
        ret := inner(n)
        fmt.Println("time spent:", time.Since(start).Seconds())
        return ret
    }
}

func slowFun(op int)int{
    time.Sleep(time.Second*1)
    return op
}

func TestFn(t *testing.T){
    a, _ := returnMultiValues()
    t.Log(a)
    tsSF := timeSpent(slowFun)
    t.Log(tsSF(10))
}

结果:

=== RUN TestFn
time spent: 1.0000582
--- PASS: TestFn (1.00s)
func_test.go:32: 1
func_test.go:34: 10
PASS

可变参数及defer

 可变参数会被转换为数组,通过数组变量来完成

func sum(ops ...int) int  {
    s := 0
    for _, op := range ops{
        s += op
    }
    return s
}

func TestParam(t *testing.T)  {
    s := sum(1, 2, 3, 4, 5)
    fmt.Println(s)
}

=== RUN TestParam
15
--- PASS: TestParam (0.00s)
PASS

defer

func clear()  {
    fmt.Println("clear resources")
}

func TestDefer(t *testing.T)  {
    defer clear()
    fmt.Println("Start")
    panic("Fatal error") //defer仍会执行
    println("Stop")
}

=== RUN TestDefer
Start
clear resources
--- FAIL: TestDefer (0.00s)
panic: Fatal error [recovered]
panic: Fatal error

会发现出现了异常,clear()依然会执行,

异常之前的fmt.Println("Start")被执行

异常之后的fmt.Println("Stop")没有被执行

原文地址:https://www.cnblogs.com/aidata/p/11875771.html