函数是能够让咱们将语句打包成一个单元,而后能够屡次调用,其能够进步利用的模块性和代码的反复利用率。

Go是编译型语言,所以函数编写的程序无关紧要;为了可读性,往往把main() 函数写在后面,其余函数依照肯定逻辑程序进行编写(例如函数被调用的程序)。

函数申明

应用关键字func申明;一个函数包含:函数名、形式参数列表、返回值、及{}函数主体,当然这些都能够省略,然而(){}不能省。

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

函数名

函数本质上也是一个援用型变量;所有命名规定和变量一样,函数名大小写对应包外是否可见,函数名也不能和常量或变量同名

const NAME  = "老王"func NAME()  {} // NAME redeclared in this block previous declaration 

参数

参数是在函数主体能够间接应用的变量

func name(x string){    fmt.Println(x) //打印x}

参数个数没有限度,类型也没有限度,形式参数有点类型变量的申明,如下:

func name(x int){} // 一个intfunc name1(x string) //一个stringfunc name2(x int,y string)//一个int,一个stringfunc name2(x , y string)//2个string

不定长参数

不定长参数是后面参数已确定,前面必须同类型的不确定长度用变量名 ... 类型示意,本质上是切片

func name(a... int){ } //不确定长度的int型 变量a 操作和切片一样func name3(a string,b... int){}

形式参数不须要和一般变量一样必须要应用

func test(a string,b int){    fmt.Println(b) // a 没有应用,能失常运行}

调用

在须要调用的中央应用函数名和参数即可

package mainimport (    "fmt")func main()  {     test("哈哈哈",2) //此处调用     test("嘿嘿嘿",3) //反复调用,应用不同的参数}func test(a string,b int){    fmt.Println(a,b)}

默认值

Go语言的函数没有默认值,函数调用是参数必须要和定义的参数统一

package mainimport (    "fmt")func main()  {     //参数给少了     test("嘿嘿嘿")  //not enough arguments in call to test     //have (string)     //want (string, int)     //参数给多了     test("嘿嘿嘿",3,5477)// too many arguments in call to test     //have (string, number, number)     //want (string, int)     //参数程序不统一     test(3,"嘿嘿嘿") //cannot use 3 (type int) as type string in argument to test     // cannot use "嘿嘿嘿" (type string) as type int in argument to test}func test(a string,b int){    fmt.Println(a,b)}

返回值

函数返回值是在参数括号前面,Go语言中函数返回值能够是多个

返回值能够定义类型,不定义具体名称

func test() string{    return "返回值"}

返回值也能够能够具体名称,超过2个字符必须加上括号()包起来

func test() (name string){     name = "返回值"     return name}

多个返回值

func test1() (name string,age int){     name = "返回值"     age = 23     return name,age}

如果有多个返回值时,定义有具体名称的必须全副都要名称:

func test1() (name string, int){     name = "返回值"     return name,23}//syntax error: mixed named and unnamed function parameters

作用域

对于整个程序来说,函数参数、返回值和函数主体里定义的参数是局部变量,函数完结后,内存会被GC回收。但GC回收是看变量是否有被援用,如果援用就不回收。如下,经常用来生成构造体变量:

package mainimport (     "fmt")type User struct {     Name string}func NewUser() *User {     return &User{}}func main()  {     user := NewUser() //此处返回局部变量,但变量不会被回收,因为还没还在援用     user.Name="嘿嘿"     fmt.Println(user)}

终止

有时候咱们须要运行到某个中央必须终止本函数,不须要等函数全副运行结束,应用关键字return来终止。

非凡函数

Go语言有一些内置函数,能间接调用的函数

函数名阐明
append、copy切片的增加元素和复制切片
close敞开管道通信
complex、real 、imag创立和操作复数
len、caplen 返回长度或数量(字符串、数组、切片、map 和管道);cap 返回容量(只能用于切片和 map)
new、make用于分配内存
panic、recover错误处理机制
print、println底层打印函数

main()函数

main函数是程序的入口,一个程序只能有一个main函数

init()函数

init函数是在编译时主动调用的函数,无需(也不能)咱们调用,一个包内能够有多个init函数,不抵触;有多个init时按导入包的程序执行,如下图按箭头程序执行。

defer

关键字defer用来提早调用函数的执行,他是在函数完结前调用,如下:

func main()  {     fmt.Println("函数开始。。。。")     defer test()     fmt.Println("函数完结。。。。")}func test() {    fmt.Println("函数完结了,我执行。。。。")}//函数开始。。。。//函数完结。。。。//函数完结了,我执行。。。。

函数完结包含整个函数执行结束和异样都触发

func main()  {     fmt.Println("函数开始。。。。")     defer test()     panic("异样")     fmt.Println("函数完结。。。。")}func test() {    fmt.Println("函数完结了,我执行。。。。")}//函数开始。。。。//函数完结了,我执行。。。。//panic:异样

能够有多个defer,执行程序是先进后出,即堆栈

package mainimport (    "fmt")func main()  {    fmt.Println("函数开始。。。。")    defer test1()    defer test2()    fmt.Println("函数完结。。。。")}func test1() {    fmt.Println("函数完结了,test1 执行。。。。")}func test2() {    fmt.Println("函数完结了,test2 执行。。。。")}//函数开始。。。。//函数完结。。。。//函数完结了,test2 执行。。。。//函数完结了,test1 执行。。。。

利用:defer 个别用于须要手动敞开的资源,如:文件的敞开、网络的敞开;还利用于解决互斥锁和错误处理。

package mainimport (     "net/http"     "sync")var mu sync.Mutexfunc main()  {     defer func() {         if err := recover();err!=nil{            //... 错误处理         }     }()     resp, err := http.Get("http://www.example.com")     if err != nil {        panic(err)     }     defer resp.Body.Close()  //网络敞开     //....}func Lock()  {     mu.Lock()     defer mu.Unlock()     //.....  临界资源的操作}

defer 只作用于语句的最初一个函数,如下:

package mainimport (    "fmt")type User struct {     Name string}func NewUser() *User {    return &User{}}func (u *User) Show(name string) *User {     fmt.Println(name)     return u}func main()  {     user := NewUser()      defer user.Show("1").Show("2").Show("3") //Show("1").Show("2") 和defer无关     user.Show("完结了。。。")}//1//2//完结了。。。//3

defer 参数是函数时

package mainimport (    "fmt")func main()  {     x,y:=1,2     defer  call(x,call(x,y)) //作为参数的call(x,y)和defer 无关,先执行     call(x,y)}func call(x ,y int) int {     fmt.Println(x,y)     return x+y}//1 2//1 2//1 3

匿名函数

匿名函数就是没有名字的函数,应用形式有两种,一种是间接调用,没有返回值;还有一种是赋值给一个变量,而后能够屡次调用,能够有返回值。如下:

package mainimport (    "fmt")func main()  {     func() {        fmt.Println("间接定义并且调用的匿名函数")  //间接定义并且调用的匿名函数     }() // 加上()间接调用         a:= func() {         fmt.Println("定义匿名函数并负责给a变量")     }     a() // 调用匿名函数  定义匿名函数并负责给a变量     a() // 调用匿名函数  定义匿名函数并负责给a变量         b:= func() int {     fmt.Println("定义匿名函数并负责给b变量,有返回值")        return 1     }     i := b() //定义匿名函数并负责给b变量,有返回值     fmt.Println(i) //1}

匿名函数的应用十分不便,经常利用于 形式参数、启动新goroutine和defer 配合处理错误等,上面简略示例

package mainimport (     "fmt")func show(y string)  {     fmt.Println(y)}func test(x string, a func(y string))  {     a(x)}func main()  {     test("形式参数",show) //形式参数         ch := make(chan struct{})     go func() {     fmt.Println("启动协程。。。")  //启动协程。。。     ch <- struct{}{}     }()     <- ch}

递归

递归函数简略来说就是本人调用本人,Go语言是反对递归函数的。上面递归求1~5数列的和

package mainimport (    "fmt")var sum intfunc main()  {     test(5)     fmt.Println(sum) //15}func test(x int)  {     if x <=0 {         return     }     sum += x     x--     test(x)  //本人调用本人}

递归函数利用宽泛,比方树的遍历、有限菜单等。尽管反对有效递归,然而递归比拟耗费内存, 应用时不应该有限递归上来,应该有个突破条件跳出。

回调

回调函数参数是另一个函数。

package mainimport (    "fmt")func main()  {     cabblak(test) //参数为函数}func test()  {     fmt.Println("参数为函数")}func cabblak(a func())  {     a()}

闭包

闭包函数是把函数作为返回值

package mainimport (     "fmt")func main()  {     i := test(1)     i() //2     i() //3         j:=test(1)     j() //2     j() //3}func test(n int) func()  {     return func() {        n++        fmt.Println(n)     }}

须要留神的是,函数实质上是援用类型变量,闭包函数返回函数即变量,这个变量还被援用状态,GC没有回收,故外面定义的各种变量内存地址没有发生变化,在前面不停反复调用i() ,地址没发生变化,所有n的值在一直累计。j是另一个变量了,和i没什么分割,就像var num1,num2 int的个num1num2关系。