golang保留的函數
init(), main()是golang的保留函數,有如下特點:
main()只能用在main包中,僅可定義一個,init()可定義任意包,可重復定義,建議只定義一個- 兩個函數定義時不能有任何返回值
- 只能由go自動調用,不可被引用
init()先於main()執行,並不能被其他函數調用,執行時按照main import順序執行。
包的執行順序
- Go的初始化和執行總是從main.main函數(main包導入其它的包)
- 同包下的不同
.go文件,按照以文件名或包路徑名的字符串順序“從小到大”排序順序執行 - 其他的包只有被
main包 import 才會執行,按照 import 的先后順序執行; - 如果某個包被多次導入的話,在執行的時候只會導入一次;
- 當一個包被導入時,如果它還導入了其它的包,則先將其它的包包含進來;
- 導入順序與初始化順序相反 main => p1 => p2 | p2 => p1 => p
- main被最后一個初始化,因其總是依賴其他包
函數
函數是將具有獨立功能的代碼組織成為一個整體,使其具有特殊功能的代碼集。在Go語言中,函數是一種數據類型,其特性有如下:
- 支持匿名函數
- 支持帶有變量名的返回值
- 支持多值返回
- 支持匿名函數
- 不支持重載,一個包中不能有兩個名字一樣的函數。
定義語法
func test(){
}
func test(a int, b int){
}
func test(a,b int){
}
func test(a,b int list...int){
}
func test(a int, b int) int{
}
func test(a int, b int ) (int,int){
}
func test(a,b int) (num int, err error){
}
花括號必須與函數聲明在同一行,這種寫法是錯誤的
func test()
{
}
命名返回值名稱
package main
import "fmt"
func test(a, b, c int) (he int, cha int) {
he = a + b + c
cha = a - b - c
return
}
func main() {
a, b := test(15, 10, 5)
fmt.Println(a)
fmt.Println(b)
}

_標識符,用來忽略返回值
函數參數傳遞方式
\1. 值傳遞
\2. 引用傳遞
注意:無論是值傳遞,還是引用傳遞,傳遞給函數的都是變量的副本,不過,值傳遞是值的持貝。引用傳遞是地址的持貝,一般來說,地址持貝更為高效。而值持貝取決於拷貝的對象大小,對象越大,則性能越低。
注意2:map、slice、chan、指針、interface默認以引用的方式傳遞
自定義函數類型
package main
import "fmt"
type ty_func func(int, int) int
func add(a, b int) int {
return a + b
}
func operator(op ty_func, a, b int) int {
return op(a, b)
}
func main() {
c := add
sum := operator(c, 100, 200)
fmt.Println(sum)
}

不定參數
- 不定參數可以通過下標/循環方式獲取參數值
- 不定參數在定義時,固定參數放前面,不定參數放后面
- 在對函數調用時,固定參數必須傳值,不定參數可以根據需要來決定是否要傳值
語法
func {func_name}({param} ...{type}){
func_body
}
參數的類型是一個 {type} 類型的集合
練習:寫一個函數add,支持1個或多個int相加,並返回相加結果
package main
import "fmt"
func test(num ...int) {
var sum int
for n := 1; n <= len(num); n++ {
sum += num[(n - 1)]
}
fmt.Println(sum)
}
func main() {
test(1)
test(1, 2, 3)
test(1, 2, 3, 4)
}

練習:寫一個函數concat,支持1個或多個string相拼接,並返回結果
func concat(age ...string) {
var str string
for _,v := range age {
str += v
}
fmt.Println(str)
}
func main() {
concat("hellow", " world", " go")
}

延遲調用defer
- 當函數返回時,執行defer語句。因此,可以用來做資源清理
- 多個defer語句,按LIFO(后進先出)的順序執行
- defer語句中的變量,在defer聲明時就決定了。
用途
- 關閉文件句柄
- 鎖資源釋放
- 數據庫連接釋放
defer語句中的變量,在defer聲明時就決定了其值
func defer_test() {
i := 0
defer fmt.Println(i)
i = 10
fmt.Println(i)
}
func main() {
defer_test()
}

多個defer按LIFO(后進先出)的順序執行
func defer_test() {
i := 0
defer fmt.Println(i)
i = 10
fmt.Println(i)
}
func main() {
defer_test()
}

defer的作用域,此處可以看到,defer的傳入不是在main的作用域下,測試可發現 defer只會在當前函數和方法返回之前被調用。
package main
import "fmt"
func main() {
fmt.Println("block starts")
{
defer fmt.Println("defer runs")
fmt.Println("block ends")
}
fmt.Println("main ends")
}

函數作用域
全局變量:既能在函數中使用,也能在其他函數中使用,可以稱為定義在函數外的變量。
局部變量:定義在函數內部的變量成為局部變量,局部變量的作用域在函數內部。
如果全局變量的名字和局部變量的名字相同,使用的是局部變量
匿名函數
package main
import "fmt"
var (
test = func(a, b int) int {
return a + b
}(10, 20)
)
var test1 = func(age ...int) int {
var sum int
for n := 0; n < len(age); n++ {
sum += age[n]
}
return sum
}
func main() {
c := test
fmt.Println(c)
d := test1(100, 100, 100)
fmt.Println(d)
}

