聲明語法
1 | func 函數名 (參數列表)(返回值列表){ |
example 1
1 | func add(){ |
- 返回值與參數皆為空
example 2
1 | func add(a int, b int){ |
- 多個參數,但無返回值
example 3
1 | func add(a int, b int)int{ |
- 多個參數,返回一個返回值,返回值的
()
省略
example 4
1 | func add(a int, b int)(int,int){ |
- 多個參數,返回多個返回值,返回值須加上
()
example 5
1 | func add(a, b int)(int,int){ |
- 多個同類型參數可一起聲明,返回多個返回值,返回值須加上
()
錯誤範例
1 | func add() |
- 函數聲明必須與
{
在同一行,否則無法編譯
函數的特點
- 函數不支持重載,一個包中不能有兩個名字一樣的函數
- 一個函數可以賦值給變量,因此函數也可以是變量
- 函數是一等公民,函數也可以是一種類型
- 支持匿名函數 (沒有名字的函數)
- 聲明時就可直接調用
- 賦值給一變量,使用變量調用
- 支持多返回值
函數可以賦值給變量
1 | package main |
[1] add函數賦值給一個變量,c變量為一指針指向函數的地址
- 其等同於:
1
2var c func(int,int)int
c = add
[2] c變量可以直接當作函數調用
result1
2
3add func address = 0x48c950
c address = 0x48c950
sum is 30
從結果可以知道add
函數跟c
變量指向的是同一個內存地址
函數亦可為類型
1 | package main |
[1] type
關鍵字可以用來定義一種自訂義的類型,其中add_func
為類型名
[2] operator
函數接受自訂義為add_func
的函數類型
[3] 如果不使用type
定義自訂義類型的話,直接於形式參數後面接上自訂義函數類型也是允許的
[4] c
變量指向的add
函數符合自訂義的add_func
這個函數類型
- 須注意傳入的參數必須符合自訂義的函數類型格式,否則會造成類型不匹配而無法編譯
result1
2sum = 300
sub = 100
匿名函數
沒有名字的函數
聲明時可直接調用
1 | package main |
[1] 在函數寫完之後直接在}
右邊加()
並填入參數調用
result1
2
3a = 10
b = 20
a + b= 30
使用變量調用
1 | package main |
[1] 在聲明變量時定義一個匿名函數
[2] 變量()
,於()
中填入參數並調用匿名函數
result1
2
3a = 10
b = 20
a + b= 30
函數參數傳遞方式
值傳遞
- 基本的數據類型皆為值傳遞
引用傳遞
- 常見的引用傳遞類型為指針(pointer)、
map
,slice
,chan
,interface
類型
- 常見的引用傳遞類型為指針(pointer)、
無論是值傳遞,還是引用傳遞,傳遞給函數的都是變量的副本,不過值傳遞是值的拷貝;引用傳遞是地址的拷貝,一般來說,地址的拷貝更為高效(最大為8 bytes),而值拷貝取決於拷貝的物件大小,物件要是越大,性能會越低
命名返回值的名字
在定義函數返回值的類型時,可將返回值對應的名字一起定義
example 1
1 | func add(a,b int)(c int){ |
example 2
1 | func calc (a,b int)(sum int, avg int) { |
_ 忽略返回值
_
標示符,用來忽略返回值1
2
3
4
5
6
7
8
9func calc (a,b int) (sum int, avg int) {
sum = a + b
avg = (a + b) / 2
return
}
func main(){
sum , _ := calc(100,200)
}
可變參數
如同最經常使用的fmt.Println()
函數
調用函數時,我們可以傳入多個參數,也可以不傳入參數
example 1
可不傳入參數或多個參數1
2
3func function_1(arg ...int) {
}
example 2
必須至少傳入一個參數或傳入多個參數1
2
3func function_1(a int, arg ...int){
}
example 3
必須至少傳入兩個參數或傳入多個參數1
2
3func function_1(a,b int, arg ...int){
}
tips
- 其中arg參數名可自訂義
- arg是一個切片(slice)
- 可通過
arg[index]
依次訪問所有參數 - 可通過
len(arg)
來判斷傳遞參數的個數
defer
特性
- 當函數返回時,會執行defer語句,因此可以用來做資源清理
- 多個defer語句,按先進後出的方式執行
- 會先執行第二次聲明的defer語句,在執行第一次執行的defer語句result
1
2
3
4
5
6
7
8
9
10
11
12
13package main
import "fmt"
func main(){
var i int = 0
defer fmt.Println(i)
defer fmt.Println("second_defer")
i = 10
fmt.Println(i)
return
}1
2
310
second_defer
0 - 先聲明的defer語句被壓至stack中,取值時從最晚聲明的defer語句開始取,所以”second defer”會先被執行
- 會先執行第二次聲明的defer語句,在執行第一次執行的defer語句
- defer語句中的變量,在defer聲明時就決定了
1 | package main |
[1] 即使i++
,但i
在defer聲明時就確定i
之值,當函數返回時打印出來的i
仍為0
result1
21
0
用途
關閉文件句柄
1 | func read(){ |
鎖資源的解放
1 | func read(){ |
數據庫連接的釋放
1 | func read(){ |