go编译器有两套:go自带的和gccgo。其中gccgo是以gcc为后端,编译后的go代码可以和
gcc编译的C/C++代码集成。go自带的编译器中有一个叫cgo的工具,可以用于在go中集成C
语言库。

在go中如果需要访问C语言函数,一般是先将C语言函数包装成go的包。前面我们已经讲了如何
构建一个包,现在我们演示如何在包中访问C语言函数。

我们现在创建和mypkg/hello功能类似的包:mypkg/hello2。hello2.go的内容如下:

 

代码:

package hello2 
    

 /* 
    
 #include 
    
 */ 
    
 import "C" 
    

 func PrintHello() { 
    
 C.puts(C.CString("Hello, world\n")) 
    
 }


在这个例子中我们使用C语言的puts函数输出结果。由于C语言不支持UTF8,我们这里只输出

英文字母。这里的import行用于引入C语言库,在该指令之前紧挨着的注释会被当作C语言编译。

然后,C语言的函数可以通过加C.前缀的方式来使用,例如:C.puts。

由于GO语言的不同数据类型之间是不能转换的,因此将go的数据类型传递给C语言的数据类型

需要强制转换。C语言的类型和C语言函数的使用方式类似,也要用C.前缀,例如:C.int、

C.float等。需要注意的是go中字符串和C语言中的字符指针是不同的,因此有一个专门的函数

C.CString用于将go字符串转换为C语言字符串。

另外,目前cgo不支持从go中访问C语言中可变参数的函数(例如printf)。如果将上目的puts

换为printf函数,那么在编译的时候可能得到以下错误提示:


代码:

unexpected type: ...

其中三个点为printf函数声明中表示可变参数的部分。

Makefile文件也要作相应的更新,主要是将GOFILES该为CGOFILES。CGOFILES对应有

C语言代码的go文件,用cgo编译。如果工程中有纯go的代码,则还是对于GOFILES。改动

后的Makefile如下:

代码:

include $(GOROOT)/src/Make.$(GOARCH) 
    

 TARG=mypkg/hello2 
    

 CGOFILES=hello2.go 
    

 include $(GOROOT)/src/Make.pkg 
    

 # Simple test programs 
    

 %: install %.go 
    
 $(GC) $*.go 
    
 $(LD) -o $@ $*.$O




go是支持垃圾内存自动回收的。对于C语言函数中申请的内存,也应该有C语言模块负责回收。

对于C.CString返回的字符串空间由go还是C语言负责回收还不清楚。


当然,将go中的数组传递给C语言也是可以的。例如,下面的代码用C语言函数打印数组:


代码:

package hello2 
    

 /* 
    
 #include 
    

 int printArray(void *p, int len) { 
    
 int *v = (int*)p; 
    
 int i; 
    
    
    
 for(i = 0; i < len; i++) { 
    
 printf("v[%d]: %d\n", i, v[i]); 
    
 } 
    
 return i; 
    
 } 
    
 */ 
    
 import "C" 
    
 import "unsafe" 
    
 import "fmt" 
    

 func PrintHello() { 
    
 C.puts(C.CString("Hello, world\n")) 
    
 } 
    

 func PrintSlice(v []int) { 
    
 n := C.printArray(unsafe.Pointer(&v[0]), C.int(len(v))) 
    
 fmt.Printf("n = %d\n", int(n)) 
    
 }



我们在import "C"前面的注释中定义了一个printArray函数,用C语言方式打印数组。

然后在PrintSlice中通过C.printArray方式调用,unsafe.Pointer(&v[0])

用于将go中的数组地址转换为C语言的void指针,C.int(len(v))指定数组的长度。


然后将函数返回值保存在变量n中,数据类型为C.int。在输出n的时候,我们强制转换为

go语言的int类型。


测试函数为hello2app.go,内容如下:


代码:

package main 
    

 import "mypkg/hello2" 
    

 func main() { 
    
 print("hello,") 
    
 hello2.PrintHello() 
    

 hello2.PrintSlice([]int{ 3, 5, 1}) 
    
 }




编译运行后输出以下结果:




代码:

[chai@localhost hello2]$ ./hello2app 
    
 hello,Hello, world 
    

 v[0]: 3 
    
 v[1]: 5 
    
 v[2]: 1 
    
 n = 3


如果我们需要在C语言函数中分配空间,然后在go中以数组的方式来访问,可以用以下方式:

代码:

/* 
    
 void* getString() { 
    
 return "test string"; 
    
 } 
    
 */ 
    
 import "C" 
    
 import "unsafe" 
    

 func TryGetString 
    
 p := (*[100]uint8)(unsafe.Pointer(C.getString())) 
    
 fmt.Printf("ss: %v\n", string(p[0:10])) 
    
 }



这样我们就可以实现go和C语言之间指针的双向传递了。出了指针之外,go中还可以访问C语言的


结构体。例如:



代码:

/* 
    
 typedef struct { 
    
 int a; 
    
 } CStruct; 
    
 */ 
    
 import "C" 
    
 import "fmt" 
    

 func TryStruct() { 
    
 v := C.CStruct{ a:10 } 
    
 fmt.Printf("v struct: %v\n", v2) 
    
 }




C语言的使用先简要介绍这么多,详细的细节清参考$GOROOT/misc/cgo/gmp/gmp.go:

http://golang.org/misc/cgo/gmp/gmp.go?h=gmp