Go语言之函数基础


一、什么是函数

为完成某一功能的程序指令(语句)的集合,称为函数。在Go中函数分为自定义函数、系统函数。

函数的基本语法:

func 函数名称(形参列表)   (返回值列表) {

    函数体...

    return 返回值列表
}
  • 形参列表 表示函数的输入
  • 函数体 表示为了实现某一功能的的代码块
  • 返回值列表 函数可以没有返回值、一个返回值、多个返回值
package main

import "fmt"

func cal(n1 float64, n2 float64, operator byte) float64 {
    
    var res float64
    switch operator {

        case '+':
            res = n1 + n2
        case '-':
            res = n1 - n2
        case '*':
            res = n1 * n2
        case '/':
            res = n1 / n2
        default:
            fmt.Println("...")
    }
    return res

}

func main() {

    res := cal(3.25, 2.16, '+') //调用函数
    fmt.Println("res=", res)
}

二、变量作用域

函数中必定涉及到一些变量,而掌握变量的作用域有利于避免代码出错。

  • 函数内部声明的变量叫做局部变量,作用域仅限于函数内部
  • 函数外部声明的变量叫做全部变量,作用域在当前整个包都有效,如果其首字母为大写,则作用域在整个程序都有效(任何包中)
  • 如果变量是在一个代码块中,如,for/if中,那么这个变量的作用域就在该段代码块

实例:

1、局部变量

package main

import "fmt"

func test() {
    // username和Age的作用域只在test函数内部
    username := "zhangsan"
    Age := 20
}

func main() {


}

2、全局变量

package main

import "fmt"

// 定义初始化全局变量,该包中所有函数都可以使用,其中Age首字母大写,其它包也可以使用
/*
这里不能写成 username := "zhangsan" 和 Age  := 20
因为等价于:
    var username string
    username = "zhangsan" 
但是第二句的赋值语句不能再函数体外,所以是错误的
*/
var username string = "zhangsan" 
var Age int = 20

func test() {
    fmt.Println("username",username)
    fmt.Println("Age",Age)
}

func main() {
    test()
    fmt.Println("main username",username)
    fmt.Println("main Age",Age)
}

3、代码块变量

package main

import "fmt"

func main() {
    // 此时变量i的作用域就是在代码块中
    for i := 0; i < 5; i++ {
        fmt.Println(i)
    }

    // 如果需要使用这个i变量可以定义成局部变量
    var i int
    for i = 0; i < 5; i++ {
        fmt.Println(i)
    }



}

三、函数参数传递方式 

 函数传递的参数方式有:

  • 值传递
  • 引用传递

无论是值传递还是引用传递,传递给函数的都是变量的副本,不同的是值传递的是值的拷贝,引用传递的是地址的拷贝,一般来说,地址拷贝的效率高,因为数据量小。

1、值类型和引用类型

值类型:基本数据类型int系列、float系列、bool、string、数组、struct结构体

引用类型:指针、slice切片、map、管道chan、interface等

值类型默认是值传递,变量直接存储值,内存通常再栈中分配:

 引用类型默认是引用传递,变量存储的是一个地址,这个地址对应的空间才真正存储数值,内存通常在堆上分配,当没有任何变量引用这个地址时,该地址对应的数据空间就会成为一个垃圾,由GC来回收。

 如果希望函数内的变量能修改函数外的变量,可以传入变量的地址&,函数内以指针的方式操作变量。

package main

import "fmt"

func test(number *int) {
    *number = 20 //修改number的值

}

func main() {
    number := 12
    test(&number) // 调用变量的值的函数
    fmt.Println("main...", number) // main... 20

}

四、注意事项

  • 函数的形参列表可以是多个,返回值列表也可以是多个
  • 形参列表和返回值列表的数据类型可以是值类型和引用类型
  • 函数的命名遵循标识符命名规范,首字母不能是数字,首字母大写该函数可以被本地文件和其它文件使用,类似public,首字母小写,只能被本包文件使用,其它包文件不能使用,类似private
  • 函数中的变量是局部,函数外不生效
  • 基本数据类型和数组默认都是值传递的,即进行值拷贝,在函数内修改,不会影响到原来的值
  • 如果希望函数内的变量能修改函数外的变量,可以传入变量的地址&,函数内以指针的方式操作变量
  • Go函数不支持函数重载(test1(n1 int)、test2(n1 int, n2 int) 这样会包函数重复定义)
  • 在Go中,函数也是一种数据类型,可以赋值给一个变量,可以通过变量对函数调用
  • 函数是一种数据类型,所以可以作为形参
  • Go中支持自定义数据类型(通过 type 自定义数据类型名 数据类型,如:type myInt int,这时myInt就等价int来使用,可以通过 var num myInt来使用)
  • 支持对函数返回值命名( test(n1 int, n2) sum int  {sum = n1+n2 return} )
  • 使用"_"标识符,忽略返回值
  • Go函数支持可变参数( 支持0到多个参数:func test(args... int){} ,支持1到多个参数:func test(n1 int, args... int){}。可以使用slice切片 args[index]取值)