Golang理解-函数声明

普通函数声明

普通函数的声明形式

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

参数类型简写

1. 同种类型的返回值

如果返回值是同种类型,则用括号将多个返回值类型括起来,用逗号分隔每个返回值类型

使用return语句时,值列表的顺序需要和函数声明的返回值类型一致

纯类型的返回值对于代码的可读性不是很友好,特别是在同类型的返回值出现时,无法区分每个返回参数的意义

func subAndSum(x, y int) (int, int) {
    sub := x - y
    sum := x + y
    return sub, sum
}
2. 带有变量名的返回值
func namedRetValues() (a, b int) {
        a = 1
        b = 2
        return
}

函数声明时将返回值命名为a,b; 因此可以在函数体中直接对函数的返回值进行赋值. 在命名的返回值方式的函数中,在函数结束前需要显示的使用return语句进行返回

同一种类型返回值和命名返回值两种形式只能二选一,混用时将会发生编译错误;例如:

func namedRetValues() (a, b int, int)

函数调用

函数调用格式: 返回值变量列表 = 函数名(参数列表)

多个返回值列表使用逗号分隔; 例如:

a, b := namedRetValues()

实例:将秒解析为时间单位

package main

// 将秒解析为时间单位

import (
        "fmt"
)

const (
    // 定义每分钟的秒数
    SecondsPerMinute = 60
    // 定义每小时的秒数
    SecondsPerHour = SecondsPerMinute * 60
    // 定义没天的秒数
    SecondsPerDay = SecondsPerHour * 24
)

// 将传入的秒解析为3种时间单位
func resolveTime(seconds int) (day, hour, minute int) {
    day = seconds / SecondsPerDay
    hour = seconds / SecondsPerHour
    minute = seconds / SecondsPerMinute
    // 显示return返回
    return
}

func main() {
    // 直接将返回值作为打印参数
    fmt.Println(resolveTime(1000))
    // 只获取小时和分钟
    _, hour, minute := resolveTime(18000)
    fmt.Println(hour, minute)
    // 只获取天
    day, _, _ := resolveTime(90000)
    fmt.Println(day)
}

函数参数传递

Go语言中传入和返回参数在调用和返回时都是值传递

这里需要注意的是指针,切片和map等引用类型对象指向的内容在参数传递中不会发生复制,而是将指针进行复制,类似创建一次引用.

实例: 测试golang中函数参数传递为值传递; 引用类型参数为一次引用创建

package main
/*
        示例: 测试golang中函数参数传递为值传递; 引用类型参数为一次引用创建
*/

import (
        "fmt"
)

// 用于测试值传递效果的结构体
type Data struct {
    complax []int       // 测试切片在参数传递中的效果, 切片是一种动态类型,内部以指针存在
    instance InnerData  // 实例分配的innerData
    ptr *InnerData      // 将ptr声明为InnerData的指针类型
}

// 代表各种结构体字段; 声明一个内嵌的结构innerData
type InnerData struct {
        a int
}

// 值传递的测试函数; 
// 该函数的参数和返回值都是Data类型,在调用中,Data的内存会被复制后传入函数,当函数返回时,又将返回值复制一次,赋值给函数返回值的接收变量
func passByByValue(inFunc Data) Data {
    // 输出参数的成员情况
    fmt.Printf("in func value: %+v\n", inFunc)
    // 打印inFunc的指针
    fmt.Printf("in func ptr: %p\n", &inFunc)     // 值传递,需要使用&符号取地址; 拥有相同地址且类型相同的变量,表示同一块内存区域

    return inFunc   // 将传入的变量作为返回值返回,返回的过程中将发生值复制
}

// 测试流程,准备一个Data格式的数据结构并填充所有成员,通过调用测试函数,传入Data结构数据,并获取返回值,对比输入和
// 输出后的Data结构数据变化,特别是指针变化情况以及输入和输出整块数据是否被复制

func main() {
    // 准备传入函数的数据结构
    in := Data{
        complax: []int{1, 2, 3},
        instance: InnerData{
          5,
        },
        ptr: &InnerData{1},
        }

    // 输入结构的成员情况
    fmt.Printf("in value: %+v\n", in)

    // 输入结构的指针地址
    fmt.Printf("in ptr: %p\n", &in)

    // 传入结构体,返回同类型的结构体
    out := passByByValue(in)

    // 输出结构的成员情况
    fmt.Printf("out value: %+v\n", out)
    // 输出结构的指针地址
    fmt.Printf("out ptr: %p\n", &out)
}

总结

  1. 所有的Data结构的指针地址发生了变化,意味着所有结构都是一块新的内存,无论是将Data结构传入函数内部,还是通过返回值传回Data都会发生复制行为
  2. 所有的Data结构中的成员值都没有发生变化,原值传递,意味着所有参数都是值传递
  3. Data结构的ptr成员在传递过程中保持一致,表示指针在函数参数值传递中传递的只是指针值,不会复制指针指向的部分

猜你喜欢

转载自www.cnblogs.com/vinsent/p/11221369.html
今日推荐