新聞中心
大家好,我是煎魚(yú)。

成都創(chuàng)新互聯(lián)主打移動(dòng)網(wǎng)站、成都網(wǎng)站建設(shè)、成都網(wǎng)站制作、網(wǎng)站改版、網(wǎng)絡(luò)推廣、網(wǎng)站維護(hù)、主機(jī)域名、等互聯(lián)網(wǎng)信息服務(wù),為各行業(yè)提供服務(wù)。在技術(shù)實(shí)力的保障下,我們?yōu)榭蛻?hù)承諾穩(wěn)定,放心的服務(wù),根據(jù)網(wǎng)站的內(nèi)容與功能再?zèng)Q定采用什么樣的設(shè)計(jì)。最后,要實(shí)現(xiàn)符合網(wǎng)站需求的內(nèi)容、功能與設(shè)計(jì),我們還會(huì)規(guī)劃穩(wěn)定安全的技術(shù)方案做保障。
最近因?yàn)楦鞣N奇怪的原因,接觸到了 Go 特色之一 CGO。這方面的相關(guān)內(nèi)容也相對(duì)少一些,給大家拋磚引玉。
圖片來(lái)源于 marlin
畢竟很多跨語(yǔ)言調(diào)用,還是會(huì)依賴(lài) CGO 這個(gè)特性。希望大家在真正要用時(shí)有個(gè)前置知識(shí)墊肚子。
CGO 是什么
CGO 就是 C 和 Go,兩個(gè)編程語(yǔ)言。指的是能夠創(chuàng)建調(diào)用 C 代碼的 Go 包。對(duì)照著 Go 代碼中的 “C”:
package main
import "C"
func main() {}一旦程序中出現(xiàn) import "C",則意味著開(kāi)啟 CGO 特性。在進(jìn)行 go build 等階段時(shí),將會(huì)調(diào)用 C 編譯器(通常是 gcc 或 clang)。
CGO 對(duì)應(yīng)的環(huán)境變量是 CGO_ENABLED,設(shè)置為 1 則開(kāi)啟 CGO,為 0 則關(guān)閉 CGO。
編譯命令如下:
CGO_ENABLED=0 go build -o hellojy main.go當(dāng)然,對(duì)于默認(rèn)值。該環(huán)境變量值為 1,C 編譯器也是使用 gcc。我們可以通過(guò) go env 看到:
一旦關(guān)閉就會(huì)影響 CGO 編譯。需要特別留意,交叉編譯時(shí)會(huì)默認(rèn)關(guān)閉 CGO。
CGO 快速上手
最小 Demo
先來(lái)一個(gè) CGO 的 Go 例子:
package main
//#include
import "C"
func main() {
s := C.CString("hello world.")
C.puts(s)
} 運(yùn)行 go run main.go,輸出結(jié)果:
hello world.聲明 C 注解
如果你沒(méi)有了解過(guò) CGO,看到上面的例子,可能會(huì)有好幾個(gè)疑問(wèn)。
首先是 include:
//#include
import "C" import "C" 我們懂,是導(dǎo)入 C 的偽包。前面的注解是什么?
無(wú)論是:
//#include 又或是:
/*
#include
#include
*/ 實(shí)際上這是導(dǎo)入 C 前的注解,注解內(nèi)容可以包含任何 C 代碼,例如:函數(shù)、變量的聲明定義、庫(kù)引用等。(該注解要緊挨導(dǎo)入語(yǔ)句)
回到 Demo 本身,如果我們?nèi)サ?nbsp;//#include
# command-line-arguments
./main.go:7:2: could not determine kind of name for C.puts去掉后,語(yǔ)句 C.puts(s) 將無(wú)法運(yùn)行。
實(shí)際上 stdio.h 的全稱(chēng)是:standard input output.header(標(biāo)準(zhǔn)輸入輸出頭文件)。該文件大都是些輸入輸出函數(shù)的聲明,引用了這庫(kù),就能使用 C 的 puts 方法。
其他同理,你在注解中聲明、定義的東西,均可以在 Go 代碼中通過(guò) C 這個(gè)偽包來(lái)引用和調(diào)用。
其次像是 CString 方法,屬于在 Go 和 C 類(lèi)型之間需要復(fù)制數(shù)據(jù)的特殊函數(shù),偽包 C 有進(jìn)行預(yù)定義。
例如:
func C.CString(string) *C.char
func C.CBytes([]byte) unsafe.Pointer
func C.GoString(*C.char) string
func C.GoStringN(*C.char, C.int) string
func C.GoBytes(unsafe.Pointer, C.int) []byteGo 和 C 類(lèi)型對(duì)照
Go 官方有提供一份基礎(chǔ)類(lèi)型的對(duì)照表,大家可以參照來(lái)使用和理解。
如下:
|
C 語(yǔ)言類(lèi)型 |
CGO 類(lèi)型 |
Go語(yǔ)言類(lèi)型 |
|
char |
C.char |
byte |
|
singed char |
C.schar |
int8 |
|
unsigned char |
C.uchar |
uint8 |
|
short |
C.short |
int16 |
|
unsigned short |
C.ushort |
uint16 |
|
int |
C.int |
int32 |
|
unsigned int |
C.uint |
uint32 |
|
long |
C.long |
int32 |
|
unsigned long |
C.ulong |
uint32 |
|
long long int |
C.longlong |
int64 |
|
unsigned long long int |
C.ulonglong |
uint64 |
|
float |
C.float |
float32 |
|
double |
C.double |
float64 |
|
size_t |
C.size_t |
uint |
注意事項(xiàng)
使用 CGO,除了會(huì)帶來(lái)一定的性能損耗外。需要特別注意的是:內(nèi)存泄露。因?yàn)?Go 是帶垃圾回收機(jī)制的編程語(yǔ)言,而使用了 C 后,需要手動(dòng)的管理內(nèi)存。
還是這個(gè) Demo:
package main
//#include
import "C"
func main() {
s := C.CString("hello world.")
C.puts(s)
} 如果這是一個(gè)常駐進(jìn)程,也沒(méi)有任何釋放動(dòng)作。用 C.CString 方法所申請(qǐng)的變量 s 就會(huì)泄露。
因此與 “C” 相關(guān)的變量創(chuàng)建,需要進(jìn)行手動(dòng)的內(nèi)存管理。正確的代碼如下:
/*
#include
#include
*/
import "C"
import (
"unsafe"
)
func main() {
b := C.CString("hello world.")
C.puts(b)
C.free(unsafe.Pointer(b))
} 需要調(diào)用 C.free 方法進(jìn)行主動(dòng)的內(nèi)存釋放。如果該程序自然結(jié)束,也會(huì)自動(dòng)回收。
總結(jié)
在今天這篇文章中,我們介紹了 Go 語(yǔ)言中 CGO 的基礎(chǔ)知識(shí)和快速入門(mén)。整體上,只要適應(yīng)了寫(xiě)法,CGO 的用法就不算太麻煩。
需要特別注意手動(dòng)內(nèi)存管理、性能損耗等多方面的制約。后續(xù)我們也會(huì)繼續(xù)深入 CGO 方面的內(nèi)容。
網(wǎng)頁(yè)標(biāo)題:快速上手GoCGO,掌握在Go里寫(xiě)C!
當(dāng)前地址:http://www.dlmjj.cn/article/cojopso.html


咨詢(xún)
建站咨詢(xún)
