函數(shù)的go語言中的一級公民,我們把所有的功能單元都定義在函數(shù)中,可以重復使用。函數(shù)包含函數(shù)的名稱、參數(shù)列表和返回值類型,這些構(gòu)成了函數(shù)的簽名(signature)。
成都創(chuàng)新互聯(lián)公司主要業(yè)務有網(wǎng)站營銷策劃、網(wǎng)站設(shè)計制作、做網(wǎng)站、微信公眾號開發(fā)、微信小程序、H5響應式網(wǎng)站、程序開發(fā)等業(yè)務。一次合作終身朋友,是我們奉行的宗旨;我們不僅僅把客戶當客戶,還把客戶視為我們的合作伙伴,在開展業(yè)務的過程中,公司還積累了豐富的行業(yè)經(jīng)驗、營銷型網(wǎng)站建設(shè)資源和合作伙伴關(guān)系資源,并逐漸建立起規(guī)范的客戶服務和保障體系。
函數(shù)在使用之前必須先定義,可以調(diào)用函數(shù)來完成某個任務。函數(shù)可以重復調(diào)用,從而達到代碼重用。
go語言函數(shù)定義語法
語法解析:
go語言函數(shù)定義實例
定義一個求和函數(shù)
定義一個比較兩個數(shù)大小的函數(shù)
go語言函數(shù)調(diào)用
當我們要完成某個任務時,可以調(diào)用函數(shù)來完成。調(diào)用函數(shù)要傳遞參數(shù),如何有返回值可以獲得返回值。
運行結(jié)果
GO是編譯性語言,所以函數(shù)的順序是無關(guān)緊要的,為了方便閱讀,建議入口函數(shù) main 寫在最前面,其余函數(shù)按照功能需要進行排列
GO的函數(shù) 不支持嵌套,重載和默認參數(shù)
GO的函數(shù) 支持 無需聲明變量,可變長度,多返回值,匿名,閉包等
GO的函數(shù)用 func 來聲明,且左大括號 { 不能另起一行
一個簡單的示例:
輸出為:
參數(shù):可以傳0個或多個值來供自己用
返回:通過用 return 來進行返回
輸出為:
上面就是一個典型的多參數(shù)傳遞與多返回值
對例子的說明:
按值傳遞:是對某個變量進行復制,不能更改原變量的值
引用傳遞:相當于按指針傳遞,可以同時改變原來的值,并且消耗的內(nèi)存會更少,只有4或8個字節(jié)的消耗
在上例中,返回值 (d int, e int, f int) { 是進行了命名,如果不想命名可以寫成 (int,int,int){ ,返回的結(jié)果都是一樣的,但要注意:
當返回了多個值,我們某些變量不想要,或?qū)嶋H用不到,我們可以使用 _ 來補位,例如上例的返回我們可以寫成 d,_,f := test(a,b,c) ,我們不想要中間的返回值,可以以這種形式來舍棄掉
在參數(shù)后面以 變量 ... type 這種形式的,我們就要以判斷出這是一個可變長度的參數(shù)
輸出為:
在上例中, strs ...string 中, strs 的實際值是b,c,d,e,這就是一個最簡單的傳遞可變長度的參數(shù)的例子,更多一些演變的形式,都非常類似
在GO中 defer 關(guān)鍵字非常重要,相當于面相對像中的析構(gòu)函數(shù),也就是在某個函數(shù)執(zhí)行完成后,GO會自動這個;
如果在多層循環(huán)中函數(shù)里,都定義了 defer ,那么它的執(zhí)行順序是先進后出;
當某個函數(shù)出現(xiàn)嚴重錯誤時, defer 也會被調(diào)用
輸出為
這是一個最簡單的測試了,當然還有更復雜的調(diào)用,比如調(diào)試程序時,判斷是哪個函數(shù)出了問題,完全可以根據(jù) defer 打印出來的內(nèi)容來進行判斷,非常快速,這種留給你們?nèi)崿F(xiàn)
一個函數(shù)在函數(shù)體內(nèi)自己調(diào)用自己我們稱之為遞歸函數(shù),在做遞歸調(diào)用時,經(jīng)常會將內(nèi)存給占滿,這是非常要注意的,常用的比如,快速排序就是用的遞歸調(diào)用
本篇重點介紹了GO函數(shù)(func)的聲明與使用,下一篇將介紹GO的結(jié)構(gòu) struct
在golang中加載dll并調(diào)用函數(shù)流程如下:
1.加載dll動態(tài)庫到內(nèi)存 syscall.LoadLibrary
2.獲取函數(shù)地址 syscall.GetProcAddress
3.執(zhí)行系統(tǒng)調(diào)用,傳入?yún)?shù) syscall.Syscall6
一個系統(tǒng)消息框函數(shù)的調(diào)用示例:
在以下這段代碼中,我們操作一個文件,無論成功與否都需要關(guān)閉文件句柄。這里在三處不同的位置都調(diào)用了file.Close()方法,代碼顯得非常冗余。
我們利用延遲調(diào)用來優(yōu)化代碼。定義后的defer代碼,會在return之前返回,讓代碼顯得更加緊湊,且可讀性變強,對上面的代碼改造如下:
我們通過這個示例來看一下延遲調(diào)用與正常代碼之間的執(zhí)行順序
先簡單分析一下代碼邏輯:
從輸出中,我們可以觀察到如下現(xiàn)象:
從這個實例中,我們很明顯觀察到,defer語句是在return之前執(zhí)行
如果一個函數(shù)內(nèi)定義了多個defer,則調(diào)用順序為LIFO(后進先出)方式執(zhí)行。
仍然是相同的例子,但是在TestDefer中我們定義了三個defer輸出,根據(jù)LIFO原則,輸出的順序是3rd-2nd-1st,根據(jù)最后的結(jié)果,也是逆向向上執(zhí)行defer輸出。
就在整理這篇筆記的時候,發(fā)現(xiàn)了自己的認知誤區(qū),主要是本節(jié)實例三中發(fā)現(xiàn)的,先來看一下英文的描述:
對于上面的這段話的理解:
下面是代碼執(zhí)行輸出,我們來一起分析一下:
雖然在a()函數(shù)內(nèi),顯示的返回了10,但是main函數(shù)中得到的結(jié)果是defer函數(shù)自增后的結(jié)果,我們來分析一下代碼:
在這篇文章的上一版,我曾經(jīng)嘗試用指針取解釋defer修改返回值的類型,但是感覺不夠透徹,也讓閱讀者非常困惑,索性參考了一下go官方blog中的一篇文章,在此基礎(chǔ)上進行了擴展。如需要閱讀原文,可以參考下面的文章。
直接嵌入c源代碼到go代碼里面
package main
/*
#include stdio.h
void myhello(int i) {
printf("Hello C: %d\n", i);
}
*/
import "C"
import "fmt"
func main() {
C.myhello(C.int(12))
fmt.Println("Hello Go");
}
需要注意的是C代碼必須放在注釋里面
import "C"語句和前面的C代碼之間不能有空行
運行結(jié)果
$ go build main.go ./main
Hello C: 12
Hello Go
分開c代碼到單獨文件
嵌在一起代碼結(jié)構(gòu)不是很好看,很多人包括我,還是喜歡把兩個分開,放在不同的文件里面,顯得干凈,go源文件里面是go的源代碼,c源文件里面是c的源代碼。
$ ls
hello.c hello.h main.go
$ cat hello.h
void hello(int);
$ cat hello.c
#include stdio.h
void hello(int i) {
printf("Hello C: %d\n", i);
}
$ cat main.go
package main
// #include "hello.h"
import "C"
import "fmt"
func main() {
C.hello(C.int(12))
fmt.Println("Hello Go");
}
編譯運行
$ go build ./main
Hello C: 12
Hello Go
編譯成庫文件
如果c文件比較多,最好還是能夠編譯成一個獨立的庫文件,然后go來調(diào)用庫。
$ find mylib main
mylib
mylib/hello.h
mylib/hello.c
main
main/main.go
編譯庫文件
$ cd mylib
# gcc -fPIC -shared -o libhello.so hello.c
編譯go程序
$ cd main
$ cat main.go
package main
// #cgo CFLAGS: -I../mylib
// #cgo LDFLAGS: -L../mylib -lhello
// #include "hello.h"
import "C"
import "fmt"
func main() {
C.hello(C.int(12))
fmt.Println("Hello Go");
}
$ go build main.go
運行
$ export LD_LIBRARY_PATH=../mylib
$ ./main
Hello C: 12
Hello Go
在我們的例子中,庫文件是編譯成動態(tài)庫的,main程序鏈接的時候也是采用的動態(tài)庫
$ ldd main
linux-vdso.so.1 = (0x00007fffc7968000)
libhello.so = ../mylib/libhello.so (0x00007f513684c000)
libpthread.so.0 = /lib64/libpthread.so.0 (0x00007f5136614000)
libc.so.6 = /lib64/libc.so.6 (0x00007f5136253000)
/lib64/ld-linux-x86-64.so.2 (0x000055d819227000)
理論上講也是可以編譯成整個一靜態(tài)鏈接的可執(zhí)行程序,由于我的機器上缺少靜態(tài)鏈接的系統(tǒng)庫,比如libc.a,所以只能編譯成動態(tài)鏈接。