再談CGO!

最近在kubernetes上搞集成其它team算法的事情拌消,他們的算法是用c寫的蚤霞,自然地我需要用cgo去調(diào)用酗失。本文整理了使用cgo過程中的一些函數(shù),特此整理下來昧绣,記錄规肴。

1,給c函數(shù)傳遞二維整型數(shù)組

c函數(shù)需要接收一個(gè)二維整型數(shù)組作為參數(shù)夜畴,

extern bool xxx_func(int** _matrix);

最初實(shí)現(xiàn)如下:

goArray := [][]int{{1},{3,3},{3,3,2}}
cArray := make([][]C.int, len(goArray))
for i, _ := range goArray { 
    cArray[i] = make([]C.int, len(goArray[i])) 
    for j, _ := range goArray[i] { 
        cArray[i][j] = C.int(goArray[i][j]) 
        }
} 
C.xxx_func((**C.int)(unsafe.Pointer(&cArray[0][0]))) // 把該數(shù)組的首地址傳遞給c函數(shù)

這樣實(shí)現(xiàn)對嗎拖刃?當(dāng)然不對! 仔細(xì)看該函數(shù)贪绘,這個(gè)函數(shù)其實(shí)是創(chuàng)建了一個(gè)切片兑牡,然后里邊保存了N個(gè)獨(dú)立的切片,它的內(nèi)存地址并不是連續(xù)的税灌!當(dāng)然這對Go本身來說是正確的均函,但是對c來說卻大錯(cuò)特錯(cuò),c中的二維數(shù)組內(nèi)存地址必須是連續(xù)的(說白了c中的二維數(shù)組其實(shí)就是個(gè)一維數(shù)組菱涤,尋址方式不同)苞也。所以應(yīng)該按照如下方式去實(shí)現(xiàn):

  • 分配一個(gè)長度為N*M的slice (N和M代表二維數(shù)組的行和列)
  • 用go的二維數(shù)組中的值填充這個(gè)slice
  • 傳遞這個(gè)slice的指針傳遞給c函數(shù)

第一種代碼實(shí)現(xiàn)如下:

n, m := len(goArray), 0
for _, row := range goArray {
    if len(row) > m {
        m = len(row)
    }
}

cArray := make([]C.int, n*m)
for i, row := range goArray {
    for j, v := range row {
        a[i * n + j] = v
    }  
}

C.xxx_func((**C.int)(unsafe.Pointer(&cArray[0]))) 

然后運(yùn)行,發(fā)現(xiàn)cannot use &cArray[0] (type *C.int) as type **C.int in argument to func literal, 上述的操作其實(shí)是把go的二維數(shù)組轉(zhuǎn)化為一維數(shù)組再傳遞給c函數(shù)狸窘,但是c函數(shù)要接收的是int**即二維數(shù)組墩朦,所以報(bào)錯(cuò)坯认,最好的解決方式是把c函數(shù)的參數(shù)改為接收int*一維數(shù)組翻擒。
But, 如果不能改c函數(shù)的參數(shù),該怎么辦呢牛哺?
第二種代碼實(shí)現(xiàn):

        cArray := make([]*C.int, len(goArray))
        for i, row := range goArray {
                p := (*C.int)(C.malloc(C.size_t(C.sizeof_int * len(row))))

                cArray[i] = p
                pa := (*[1 << 30]C.int)(unsafe.Pointer(p))
                for j, v := range row {
                        (*pa)[j] = C.int(v)
                }
        }

這里陋气,我們?yōu)間oArray的每一行分配一個(gè)源大小的C.int數(shù)組,并給將指針p付給cArray,
然后把p類型*C.int 轉(zhuǎn)化為*[1<<30]C.int類型(指向一個(gè)很大的C.int型數(shù)組)引润,但是這里需要注意兩個(gè)問題:

  • C.malloc()的內(nèi)存釋放
  • 我們給每一行row分配了一個(gè)數(shù)組巩趁,但是數(shù)組長度信息丟失了!在go這邊沒問題,但是對于c來說议慰,一個(gè)數(shù)組僅僅是一個(gè)指針蠢古,它通過這個(gè)指針沒法知道指向這個(gè)內(nèi)存區(qū)域的長度,你得告訴它别凹,或者寫個(gè)包含該指針和長度的結(jié)構(gòu)體草讶,這個(gè)依賴于c那邊的具體實(shí)現(xiàn)

推薦使用第一種方法,因?yàn)閏go和c的內(nèi)存釋放問題就能折騰你好幾天炉菲,除非你清楚地知道堕战,哪里該釋放,哪里不應(yīng)該釋放拍霜。

2嘱丢,給c函數(shù)傳遞字符串?dāng)?shù)組

這個(gè)簡單多了,但是需要注意內(nèi)存釋放祠饺。代碼如下:

goString := []string{"w0", "w1", "w2", "w3"} 
cString := make([]*C.char, len(goString)) 
for i, _ := range goString { 
        cString[i] = C.CString(goString[i]) 

        // 注意這個(gè)內(nèi)存釋放越驻,該不該在調(diào)用完c函數(shù)之后釋放,依賴于c那邊的實(shí)現(xiàn)道偷,這里要小心使用伐谈!
        defer C.free(unsafe.Pointer(cString[i])) 
} 

C.xxx_func2((**C.char)(unsafe.Pointer(&cString[0])))

關(guān)于C.CString的官方文檔:

// Go string to C string
// The C string is allocated in the C heap using malloc.
// It is the caller's responsibility to arrange for it to be
// freed, such as by calling C.free (be sure to include stdlib.h
// if C.free is needed).
func C.CString(string) *C.char

3,把c字符串?dāng)?shù)組轉(zhuǎn)化為go字符串切片

//你必須清楚地知道返回的c字符串?dāng)?shù)組的長度
func GoStrings(length int, argv **C.char) []string {
        if argv == nil {
                return nil
        }
        tmpslice := (*[1 << 30]*C.char)(unsafe.Pointer(argv))[:length:length]
        gostrings := make([]string, length)
        for i, s := range tmpslice {
                gostrings[i] = C.GoString(s)
        }
        return gostrings
}

C.GoString的官方文檔试疙,真簡潔:(

// C string to Go string
func C.GoString(*C.char) string

參考資料:https://golang.org/cmd/cgo/
cgo wiki : https://github.com/golang/go/wiki/cgo
google大神多: https://groups.google.com/forum/#!topic/golang-nuts/Nb-nfVdAyF0

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末诵棵,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子祝旷,更是在濱河造成了極大的恐慌履澳,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件怀跛,死亡現(xiàn)場離奇詭異距贷,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)吻谋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門忠蝗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人漓拾,你說我怎么就攤上這事阁最。” “怎么了骇两?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵速种,是天一觀的道長。 經(jīng)常有香客問我低千,道長配阵,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮棋傍,結(jié)果婚禮上救拉,老公的妹妹穿的比我還像新娘。我一直安慰自己瘫拣,他們只是感情好近上,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著拂铡,像睡著了一般壹无。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上感帅,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天斗锭,我揣著相機(jī)與錄音,去河邊找鬼失球。 笑死岖是,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的实苞。 我是一名探鬼主播豺撑,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼黔牵!你這毒婦竟也來了聪轿?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤猾浦,失蹤者是張志新(化名)和其女友劉穎陆错,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體金赦,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡音瓷,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了夹抗。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片绳慎。...
    茶點(diǎn)故事閱讀 40,040評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖漠烧,靈堂內(nèi)的尸體忽然破棺而出杏愤,到底是詐尸還是另有隱情,我是刑警寧澤沽甥,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布声邦,位于F島的核電站,受9級特大地震影響摆舟,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一恨诱、第九天 我趴在偏房一處隱蔽的房頂上張望媳瞪。 院中可真熱鬧,春花似錦照宝、人聲如沸蛇受。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽兢仰。三九已至,卻和暖如春剂碴,著一層夾襖步出監(jiān)牢的瞬間把将,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工忆矛, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留察蹲,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓催训,卻偏偏與公主長得像洽议,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子漫拭,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容