教程集 www.jiaochengji.com
教程集 >  Golang编程  >  golang教程  >  正文 Go基础编程:函数

Go基础编程:函数

发布时间:2022-01-23   编辑:jiaochengji.com
教程集为您提供Go基础编程:函数等资源,欢迎您收藏本站,我们将为您提供最新的Go基础编程:函数资源

函数是可以让我们将语句打包成一个单元,然后可以多次调用,其可以提高应用的模块性和代码的重复利用率。

Go是编译型语言,所以函数编写的顺序无关紧要;为了可读性,往往把<code>main()</code> 函数写在前面,其他函数按照一定逻辑顺序进行编写(例如函数被调用的顺序)。

函数声明

使用关键字<code>func</code>声明;一个函数包括:函数名、形式参数列表、返回值、及<code>{}</code>函数主体,当然这些都可以省略,但是<code>()</code>、<code>{}</code>不能省。

<pre><code class="lang-go hljs">func 函数名(参数列表) 返回值{ 主体 }</code></code></pre>

函数名

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

<pre><code class="lang-go hljs">const NAME  = "老王" ​ func NAME() {} // NAME redeclared in this block previous declaration </code></code></pre>

参数

参数是在函数主体可以直接使用的变量

<pre><code class="lang-go hljs">func name(x string){ fmt.Println(x) //打印x }</code></code></pre>

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

<pre><code class="lang-go hljs">func name(x int){} // 一个int func name1(x string) //一个string func name2(x int,y string)//一个int,一个string func name2(x , y string)//2个string</code></code></pre>

不定长参数

不定长参数是前面参数已确定,后面必须同类型的不确定长度用<code>变量名 ... 类型</code>表示,实质上是切片

<pre><code class="lang-go hljs">func name(a... int){ } //不确定长度的int型 变量a 操作和切片一样 ​ func name3(a string,b... int){}</code></code></pre>

形式参数不需要和普通变量一样必须要使用

<pre><code class="lang-go hljs">func test(a string,b int){ fmt.Println(b) // a 没有使用,能正常运行 }</code></code></pre>

调用

在需要调用的地方使用函数名和参数即可

<pre><code class="lang-go hljs">package main ​ import ( "fmt" ) ​ func main() { test("哈哈哈",2) //此处调用 test("嘿嘿嘿",3) //重复调用,使用不同的参数 } ​ func test(a string,b int){ fmt.Println(a,b) }</code></code></pre>

默认值

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

<pre><code class="lang-go hljs">package main ​ import ( "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) }</code></code></pre>

返回值

函数返回值是在参数括号后面,Go语言中函数返回值可以是多个

返回值可以定义类型,不定义具体名称

<pre><code class="lang-go hljs">func test() string{ return "返回值" }</code></code></pre>

返回值也可以可以具体名称,超过2个字符必须加上括号<code>()</code>包起来

<pre><code class="lang-go hljs">func test() (name string){ name = "返回值" return name }</code></code></pre>

多个返回值

<pre><code class="lang-go hljs">func test1() (name string,age int){ name = "返回值" age = 23 return name,age }</code></code></pre>

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

<pre><code class="lang-go hljs">func test1() (name string, int){ name = "返回值" return name,23 } //syntax error: mixed named and unnamed function parameters</code></code></pre>

作用域

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

<pre><code class="lang-go hljs">package main ​ import ( "fmt" ) ​ type User struct { Name string } ​ func NewUser() *User { return &User{} } ​ func main() { user := NewUser() //此处返回局部变量,但变量不会被回收,因为还没还在引用 user.Name="嘿嘿" fmt.Println(user) }</code></code></pre>

终止

有时候我们需要运行到某个地方必须终止本函数,不需要等函数全部运行完毕,使用关键字<code>return</code>来终止。

特殊函数

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

<table><thead><tr><th>函数名</th><th>说明</th></tr></thead><tbody><tr><td>append、copy</td><td>切片的添加元素和复制切片</td></tr><tr><td>close</td><td>关闭管道通信</td></tr><tr><td>complex、real 、imag</td><td>创建和操作复数</td></tr><tr><td>len、cap</td><td>len 返回长度或数量(字符串、数组、切片、map 和管道);cap 返回容量(只能用于切片和 map)</td></tr><tr><td>new、make</td><td>用于分配内存</td></tr><tr><td>panic、recover</td><td>错误处理机制</td></tr><tr><td>print、println</td><td>底层打印函数</td></tr></tbody></table>

<code>main()</code>函数

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

<code>init()</code>函数

<code>init</code>函数是在编译时自动调用的函数,无需(也不能)我们调用,一个包内可以有<code>多个init</code>函数,不冲突;有多个<code>init</code>时按导入包的顺序执行,如下图按箭头顺序执行。

<span class="img-wrap"></span>

defer

关键字<code>defer</code>用来延迟调用函数的执行,他是在函数结束前调用,如下:

<pre><code class="lang-go hljs">func main() { fmt.Println("函数开始。。。。") defer test() fmt.Println("函数结束。。。。") } ​ func test() { fmt.Println("函数结束了,我执行。。。。") } //函数开始。。。。 //函数结束。。。。 //函数结束了,我执行。。。。</code></code></pre>

函数结束包括整个函数执行完毕和异常都触发

<pre><code class="lang-go hljs">func main() { fmt.Println("函数开始。。。。") defer test() panic("异常") fmt.Println("函数结束。。。。") } ​ func test() { fmt.Println("函数结束了,我执行。。。。") } //函数开始。。。。 //函数结束了,我执行。。。。 //panic:异常 </code></code></pre>

可以有多个defer,执行顺序是<code>先进后出</code>,即堆栈

<pre><code class="lang-go hljs">package main ​ import ( "fmt" ) ​ func main() { fmt.Println("函数开始。。。。") defer test1() defer test2() fmt.Println("函数结束。。。。") } ​ func test1() { fmt.Println("函数结束了,test1 执行。。。。") } ​ func test2() { fmt.Println("函数结束了,test2 执行。。。。") } ​ //函数开始。。。。 //函数结束。。。。 //函数结束了,test2 执行。。。。 //函数结束了,test1 执行。。。。</code></code></pre>

应用:defer 一般用于需要手动关闭的资源,如:文件的关闭、网络的关闭;还应用于处理互斥锁和错误处理。

<pre><code class="lang-go hljs">package main ​ import ( "net/http" "sync" ) var mu sync.Mutex func 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() //..... 临界资源的操作 }</code></code></pre>

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

<pre><code class="lang-go hljs">package main ​ import ( "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</code></code></pre>

defer 参数是函数时

<pre><code class="lang-go hljs">package main ​ import ( "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</code></code></pre>

匿名函数

匿名函数就是没有名字的函数,使用方式有两种,一种是直接调用,没有返回值;还有一种是赋值给一个变量,然后可以多次调用,可以有返回值。如下:

<pre><code class="lang-go hljs">package main ​ import ( "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 } ​</code></code></pre>

匿名函数的使用非常方便,常常应用于 形式参数、启动新<code>goroutine</code>和defer 配合处理错误等,下面简单示例

<pre><code class="lang-go hljs">package main ​ import ( "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 }</code></code></pre>

递归

递归函数简单来说就是<code>自己调用自己</code>,Go语言是支持递归函数的。下面递归求1~5数列的和

<pre><code class="lang-go hljs">package main ​ import ( "fmt" ) var sum int func main() { test(5) fmt.Println(sum) //15 } ​ func test(x int) { if x <=0 { return } sum = x x-- test(x)  //自己调用自己 }</code></code></pre>

递归函数应用广泛,比如树的遍历、无限菜单等。虽然支持无效递归,但是递归比较消耗内存, 使用时不应该无限递归下去,应该有个打破条件跳出。

回调

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

<pre><code class="lang-go hljs">package main ​ import ( "fmt" ) ​ func main() { cabblak(test) //参数为函数 } ​ func test() { fmt.Println("参数为函数") } ​ func cabblak(a func()) { a() }</code></code></pre>

闭包

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

<pre><code class="lang-go hljs">package main ​ import ( "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) } } ​</code></code></pre>

需要注意的是,函数本质上是引用类型变量,闭包函数返回函数即变量,这个变量还被引用状态,GC没有回收,故里面定义的各种变量内存地址没有发生变化,在后面不停重复调用<code>i()</code> ,地址没发生变化,所有<code>n</code>的值在不断累计。<code>j</code>是另一个变量了,和<code>i</code>没什么联系,就像<code>var num1,num2 int</code>的个<code>num1</code>和<code>num2</code>关系。


到此这篇关于“Go基础编程:函数”的文章就介绍到这了,更多文章或继续浏览下面的相关文章,希望大家以后多多支持JQ教程网!

您可能感兴趣的文章:
GO语言零基础从入门到精通视频教程
Go 零基础编程入门教程
想系统学习GO语言(Golang
2018年最全Go语言教程零基础入门到进阶实战视频
golang基础教程
Go语言发展历史、核心、特性及学习路线
应用编程基础课第三讲:Go编程基础
go语言基础语法
从零开始学习GO语言-搭建Go语言开发环境-快速开发入门第一个小程序
Go编程基础4-Http服务器

[关闭]
~ ~