天天看點

cgo的踩坑記及一個簡單的例子1. 對照表2. 隐藏的坑

這裡寫自定義目錄标題

  • 1. 對照表
  • 2. 隐藏的坑
    • 2.1 嵌入式
    • 2.2 獨立式

1. 對照表

首先,來看 golang 和 C 的類型轉換對照表:

C語言類型 CGO類型 Go語言類型
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

值得注意的是,C 中的整形比如 int 在标準中是沒有定義具體字長的,但一般預設認為是 4 位元組,對應 CGO 類型中 C.int 則明确定義了字長是 4 ,但 golang 中的 int 字長則是 8 ,是以對應的 golang 類型不是 int 而是 int32 。為了避免誤用,C 代碼最好使用 C99 标準的數值類型,對應的轉換關系如下:

C語言類型 CGO類型 Go語言類型
int8_t C.int8_t int8
uint8_t C.uint8_t uint8
int16_t C.int16_t int16
uint16_t C.uint16_t uint16
int32_t C.int32_t int32
uint32_t C.uint32_t uint32
int64_t C.int64_t int64
uint64_t C.uint64_t uint64

2. 隐藏的坑

在Go中調用C一共有兩種辦法:

  • 第一種是将C代碼直接嵌入到GO源檔案中
  • 第二種是将C代碼寫在C檔案中,再在GO檔案中引入

2.1 嵌入式

第一種實作起來比較順滑,如絲一般,但是沒有進行分檔案夾管理,看上去很不爽,同時也隐藏了一個坑,直接看一個栗子:

package main

/*
#include <stdio.h>

static int32_t add(int32_t a, int32_t b){
  return a+b;
}
*/
import "C"
import "fmt"

func main() {
  var a, b int32 = 1, 2
  c := int32(C.add(C.int32_t(a), C.int32_t(b)))
  fmt.Println(c)
}
           

如果在c代碼與“import “C””之間留一個空行,直接報錯如下:

cgo的踩坑記及一個簡單的例子1. 對照表2. 隐藏的坑

去掉空行後就ok啦~,結果如下:

cgo的踩坑記及一個簡單的例子1. 對照表2. 隐藏的坑

2.2 獨立式

第二種實作實作起來坑就大大的了,no bb, 直接上栗子:

cgo的踩坑記及一個簡單的例子1. 對照表2. 隐藏的坑
//foo.c
#include <stdio.h>

//int count = 6;
void foo() {
    printf("I am foo!\n");
}
           
//foo.h

int count = 6;
void foo();
           
package main

// #include <stdio.h>
// #include <stdlib.h>
// #include "foo.h"
import "C"
import "fmt"

func main() {
  fmt.Println(C.count)
  C.foo()
}
           

佛曰:衆生都是這麼幹的,一般分為兩步:

  • 将C檔案編譯成動态連結庫libfoo.so
  • 使用指令go build foo.go編譯生成可執行檔案

但在第二步的編譯過程中會出現如下“對foo未定義的引用“的錯誤:

cgo的踩坑記及一個簡單的例子1. 對照表2. 隐藏的坑

于是乎,趕緊引經據典,經過一番折騰之後,在https://groups.google.com/forum/#!topic/golang-nuts/O4RTszIyL7c最終找到了答案:

我們不需要手動地給go提供動态連結庫,甚至于go根本不認識我們自己手動編譯出的動态連結庫。其實cgo提供了一種機制:它能夠根據import”C”中引入的頭檔案,自動找到相應的源檔案進行編譯連結。這種機制的調用,需要用到go build指令。

輸出結果如下:

cgo的踩坑記及一個簡單的例子1. 對照表2. 隐藏的坑

佛曰:衆生皆幻象,衆生皆平等!