重慶分公司,新征程啟航
為企業(yè)提供網(wǎng)站建設(shè)、域名注冊(cè)、服務(wù)器等服務(wù)
為企業(yè)提供網(wǎng)站建設(shè)、域名注冊(cè)、服務(wù)器等服務(wù)
直接嵌入c源代碼到go代碼里面
網(wǎng)站建設(shè)哪家好,找創(chuàng)新互聯(lián)建站!專(zhuān)注于網(wǎng)頁(yè)設(shè)計(jì)、網(wǎng)站建設(shè)、微信開(kāi)發(fā)、微信小程序開(kāi)發(fā)、集團(tuán)企業(yè)網(wǎng)站建設(shè)等服務(wù)項(xiàng)目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了興隆免費(fèi)建站歡迎大家使用!
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"語(yǔ)句和前面的C代碼之間不能有空行
運(yùn)行結(jié)果
$ go build main.go ./main
Hello C: 12
Hello Go
分開(kāi)c代碼到單獨(dú)文件
嵌在一起代碼結(jié)構(gòu)不是很好看,很多人包括我,還是喜歡把兩個(gè)分開(kāi),放在不同的文件里面,顯得干凈,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");
}
編譯運(yùn)行
$ go build ./main
Hello C: 12
Hello Go
編譯成庫(kù)文件
如果c文件比較多,最好還是能夠編譯成一個(gè)獨(dú)立的庫(kù)文件,然后go來(lái)調(diào)用庫(kù)。
$ find mylib main
mylib
mylib/hello.h
mylib/hello.c
main
main/main.go
編譯庫(kù)文件
$ 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
運(yùn)行
$ export LD_LIBRARY_PATH=../mylib
$ ./main
Hello C: 12
Hello Go
在我們的例子中,庫(kù)文件是編譯成動(dòng)態(tài)庫(kù)的,main程序鏈接的時(shí)候也是采用的動(dòng)態(tài)庫(kù)
$ 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)
理論上講也是可以編譯成整個(gè)一靜態(tài)鏈接的可執(zhí)行程序,由于我的機(jī)器上缺少靜態(tài)鏈接的系統(tǒng)庫(kù),比如libc.a,所以只能編譯成動(dòng)態(tài)鏈接。
直接調(diào)用so的函數(shù)cgo應(yīng)該繞不開(kāi)吧,我寫(xiě)過(guò)一個(gè)銀行的應(yīng)用程序調(diào)用其特色業(yè)務(wù)接口,因?yàn)榻涌谥恢С謈和java,我就封裝了一個(gè)c的so,然后用cgo調(diào)用后寫(xiě)了一個(gè)RPC供遠(yuǎn)程的go語(yǔ)言調(diào)用,因?yàn)镽PC只負(fù)責(zé)信息交互不負(fù)責(zé)業(yè)務(wù)邏輯,所以寫(xiě)了不到百行,以后基本不用再改。記住雖然go語(yǔ)言自帶gc,但cgo還是要手工釋放內(nèi)存哦。
Cgo 使得Go程序能夠調(diào)用C代碼. cgo讀入一個(gè)用特別的格式寫(xiě)的Go語(yǔ)言源文件, 輸出Go和C程序, 使得C程序能打包到Go語(yǔ)言的程序包中.
舉例說(shuō)明一下. 下面是一個(gè)Go語(yǔ)言包, 包含了兩個(gè)函數(shù) -- Random 和 Seed -- 是C語(yǔ)言庫(kù)中random和srandom函數(shù)的馬甲.
package rand
/*
#include stdlib.h
*/ import "C" func Random() int { return int(C.random()) } func Seed(i int) { C.srandom(C.uint(i)) }
我們來(lái)看一下這里都有什么內(nèi)容. 開(kāi)始是一個(gè)包的導(dǎo)入語(yǔ)句.
rand包導(dǎo)入了"C"包, 但你會(huì)發(fā)現(xiàn)在Go的標(biāo)準(zhǔn)庫(kù)里沒(méi)有這個(gè)包. 那是因?yàn)镃是一個(gè)"偽包", 一個(gè)為cgo引入的特殊的包名, 它是C命名空間的一個(gè)引用.
rand 包包含4個(gè)到C包的引用: 調(diào)用 C.random和C.srandom, 類(lèi)型轉(zhuǎn)換 C.uint(i)還有引用語(yǔ)句.
Random函數(shù)調(diào)用libc中的random函數(shù), 然后回返結(jié)果. 在C中, random返回一個(gè)C類(lèi)型的長(zhǎng)整形值, cgo把它輪換為C.long. 這個(gè)值必需轉(zhuǎn)換成Go的類(lèi)型, 才能在Go程序中使用. 使用一個(gè)常見(jiàn)的Go類(lèi)型轉(zhuǎn)換:
func Random() int { return int(C.random()) }
這是一個(gè)等價(jià)的函數(shù), 使用了一個(gè)臨時(shí)變量來(lái)進(jìn)行類(lèi)型轉(zhuǎn)換:
func Random() int { var r C.long = C.random() return int(r) }
Seed函數(shù)則相反. 它接受一個(gè)Go語(yǔ)言的int類(lèi)型, 轉(zhuǎn)換成C語(yǔ)言的unsigned int類(lèi)型, 然后傳遞給C的srandom函數(shù).
func Seed(i int) { C.srandom(C.uint(i)) }
需要注意的是, cgo中的unsigned int類(lèi)型寫(xiě)為C.uint; cgo的文檔中有完整的類(lèi)型列表.
這個(gè)例子中還有一個(gè)細(xì)節(jié)我們沒(méi)有說(shuō)到, 那就是導(dǎo)入語(yǔ)句上面的注釋.
/*
#include stdlib.h
*/ import "C"
Cgo可以識(shí)別這個(gè)注釋, 并在編譯C語(yǔ)言程序的時(shí)候?qū)⑺?dāng)作一個(gè)頭文件來(lái)處理. 在這個(gè)例子中, 它只是一個(gè)include語(yǔ)句, 然而其實(shí)它可以是使用有效的C語(yǔ)言代碼. 這個(gè)注釋必需緊靠在import "C"這個(gè)語(yǔ)句的上面, 不能有空行, 就像是文檔注釋一樣.
Strings and things
與Go語(yǔ)言不同, C語(yǔ)言中沒(méi)有顯式的字符串類(lèi)型. 字符串在C語(yǔ)言中是一個(gè)以0結(jié)尾的字符數(shù)組.
Go和C語(yǔ)言中的字符串轉(zhuǎn)換是通過(guò)C.CString, C.GoString,和C.GoStringN這些函數(shù)進(jìn)行的. 這些轉(zhuǎn)換將得到字符串類(lèi)型的一個(gè)副本.
下一個(gè)例子是實(shí)現(xiàn)一個(gè)Print函數(shù), 它使用C標(biāo)準(zhǔn)庫(kù)中的fputs函數(shù)把一個(gè)字符串寫(xiě)到標(biāo)準(zhǔn)輸出上:
package print // #include stdio.h // #include stdlib.h import "C" import "unsafe" func Print(s string) { cs := C.CString(s) C.fputs(cs, (*C.FILE)(C.stdout)) C.free(unsafe.Pointer(cs)) }
在C程序中進(jìn)行的內(nèi)存分配是不能被Go語(yǔ)言的內(nèi)存管理器感知的. 當(dāng)你使用C.CString創(chuàng)建一個(gè)C字符串時(shí)(或者其它類(lèi)型的C語(yǔ)言內(nèi)存分配), 你必需記得在使用完后用C.free來(lái)釋放它.
調(diào)用C.CString將返回一個(gè)指向字符數(shù)組開(kāi)始處的指錯(cuò), 所以在函數(shù)退出前我們把它轉(zhuǎn)換成一個(gè)unsafe.Pointer(Go中與C的void 等價(jià)的東西), 使用C.free來(lái)釋放分配的內(nèi)存. 一個(gè)慣用法是在分配內(nèi)存后緊跟一個(gè)defer(特別是當(dāng)這段代碼比較復(fù)雜的時(shí)候), 這樣我們就有了下面這個(gè)Print函數(shù):
func Print(s string) { cs := C.CString(s) defer C.free(unsafe.Pointer(cs)) C.fputs(cs, (*C.FILE)(C.stdout)) }
構(gòu)建 cgo 包
如果你使用goinstall, 構(gòu)建cgo包就比較容易了, 只要調(diào)用像平常一樣使用goinstall命令, 它就能自動(dòng)識(shí)別這個(gè)特殊的import "C", 然后自動(dòng)使用cgo來(lái)編譯這些文件.
如果你想使用Go的Makefiles來(lái)構(gòu)建, 那在CGOFILES變量中列出那些要用cgo處理的文件, 就像GOFILES變量包含一般的Go源文件一樣.
rand包的Makefile可以寫(xiě)成下面這樣:
include $(GOROOT)/src/Make.inc
TARG=goblog/rand
CGOFILES=\ rand.go\ include $(GOROOT)/src/Make.pkg
然后輸入gomake開(kāi)始構(gòu)建.
更多 cgo 的資源
cgo的文檔中包含了關(guān)于C偽包的更多詳細(xì)的說(shuō)明, 以及構(gòu)建過(guò)程. Go代碼樹(shù)中的cgo的例子給出了更多更高級(jí)的用法.
一個(gè)簡(jiǎn)單而又符合Go慣用法的基于cgo的包是Russ Cox寫(xiě)的gosqlite. 而Go語(yǔ)言的網(wǎng)站上也列出了更多的的cgo包.
最后, 如果你對(duì)于cgo的內(nèi)部是怎么運(yùn)作這個(gè)事情感到好奇的話, 去看看運(yùn)行時(shí)包的cgocall.c文件的注釋吧.
工程結(jié)構(gòu)如上圖所示,我們需要實(shí)現(xiàn)的目標(biāo)是在go文件中調(diào)用c文件
foo.c如下:
foo.go如下
foo.h如下:
編譯過(guò)程如下:
1、先將c文件編譯為.o文件,然后生成動(dòng)態(tài)鏈接庫(kù).dylib文件
(1) clang -c foo.c
(2 clang -shared foo.o -o libfoo.dylib
2、在上述的動(dòng)態(tài)鏈接庫(kù)生成之后,在foo.go中添加動(dòng)態(tài)鏈接命令:#cgo LDFLAGS: -L./ -lfoo
需要注意的是
中間不能有空格
Go語(yǔ)言是谷歌推出的一種全新的編程語(yǔ)言,可以在不損失應(yīng)用程序性能的情況下降低代碼的復(fù)雜性。谷歌首席軟件工程師羅布派克(Rob Pike)說(shuō):我們之所以開(kāi)發(fā)Go,是因?yàn)檫^(guò)去10多年間軟件開(kāi)發(fā)的難度令人沮喪。
Go是谷歌2009發(fā)布的第二款編程語(yǔ)言。2009年7月份,谷歌曾發(fā)布了Simple語(yǔ)言,它是用來(lái)開(kāi)發(fā)Android應(yīng)用
Go Logo
的一種BASIC語(yǔ)言.
北京時(shí)間2010年1月10日,Go語(yǔ)言摘得了TIOBE公布的2009年年度大獎(jiǎng)。該獎(jiǎng)項(xiàng)授予在2009年市場(chǎng)份額增長(zhǎng)最多的編程語(yǔ)言。
谷歌資深軟件工程師羅布·派克(Rob Pike)表示,“Go讓我體驗(yàn)到了從未有過(guò)的開(kāi)發(fā)效率。”派克表示,和今天的C++或C一樣,Go是一種系統(tǒng)語(yǔ)言。他解釋道,“使用它可以進(jìn)行快速開(kāi)發(fā),同時(shí)它還是一個(gè)真正的編譯語(yǔ)言,我們之所以現(xiàn)在將其開(kāi)源,原因是我們認(rèn)為它已經(jīng)非常有用和強(qiáng)大。”
2007年,谷歌把Go作為一個(gè)20%項(xiàng)目開(kāi)始研發(fā),即讓員工抽出本職工作之外時(shí)間的20%, 投入在該項(xiàng)目上。除了派克外,該項(xiàng)目的成員還有其他谷歌工程師也參與研發(fā)。
派克表示,編譯后Go代碼的運(yùn)行速度與C語(yǔ)言非常接近,而且編譯速度非常快,就像在使用一個(gè)交互式語(yǔ)言。現(xiàn)有編程語(yǔ)言均未專(zhuān)門(mén)對(duì)多核處理器進(jìn)行優(yōu)化。Go就是谷歌工程師為這類(lèi)程序編寫(xiě)的一種語(yǔ)言。它不是針對(duì)編程初學(xué)者設(shè)計(jì)的,但學(xué)習(xí)使用它也不是非常困難。Go支持面向?qū)ο螅揖哂姓嬲拈]包(closures)和反射 (reflection)等功能。
在學(xué)習(xí)曲線方面,派克認(rèn)為Go與Java類(lèi)似,對(duì)于Java開(kāi)發(fā)者來(lái)說(shuō),應(yīng)該能夠輕松學(xué)會(huì) Go。之所以將Go作為一個(gè)開(kāi)源項(xiàng)目發(fā)布,目的是讓開(kāi)源社區(qū)有機(jī)會(huì)創(chuàng)建更好的工具來(lái)使用該語(yǔ)言,例如 Eclipse IDE中的插件。
在谷歌公開(kāi)發(fā)布的所有網(wǎng)絡(luò)應(yīng)用中,均沒(méi)有使用Go,但是谷歌已經(jīng)使用該語(yǔ)言開(kāi)發(fā)了幾個(gè)內(nèi)部項(xiàng)目。派克表示,Go是否會(huì)對(duì)谷歌即將推出的Chrome OS產(chǎn)生影響,還言之尚早,不過(guò)Go的確可以和Native Client配合使用。他表示“Go可以讓?xiě)?yīng)用完美的運(yùn)行在瀏覽器內(nèi)。”例如,使用Go可以更高效的實(shí)現(xiàn)Wave,無(wú)論是在前端還是后臺(tái)。
Go 同時(shí)具有兩種編譯器,一種是建立在GCC基礎(chǔ)上的Gccgo,另外一種是分別針對(duì)64位x64和32位x86計(jì)算機(jī)的一套編譯器(6g和8g)。谷歌目前正在研發(fā)其對(duì)ARM芯片和Android設(shè)備的支持。派克表示,“Android手機(jī)存在的問(wèn)題是,我們一直沒(méi)有一個(gè)數(shù)學(xué)協(xié)處理器。
利用 Go 和 C 語(yǔ)言的交互,我們可以寫(xiě) go 的 c 擴(kuò)展。
新建一個(gè)文件 greeter.h , 保存內(nèi)容如下:
新建一個(gè)文件 greeter.c , 保存內(nèi)容如下:
新建一個(gè)文件 greeter.go , 保存內(nèi)容如下:
這一有個(gè)注意事項(xiàng):跟 C 相關(guān)的一定要放在 import "C" 上面。
我之前就是沒(méi)有放上面,一直沒(méi)成功。