要安装gcc支持,同时go开始CGO_ENABLED=1环境变量
注意事项:
在go语言main中,#include的c语言代码和import “C"中间不能有空行如下这样报错
//#include <stdio.h>
import "C"
使用C语言标准头文件 #
引用C标准输入输出头文件<stdio.h>,直接使用c语言函数puts操作
调用文件chapter01/chapter01.go
package main
//#include <stdio.h>
import "C"
func main() {
// C.CString Go语言string 转化为C string
// C.puts 标准输出
C.puts(C.CString("hello stdio.h puts\n"))
}
使用C语言头文件,封装方法 #
重新定义了SayHello方法,函数内使用stdio.h的puts方法
调用文件chapter02/chapter02.go
package main
/*
#include <stdio.h>
static void SayHello(const char* s){
puts(s); //c语句要有;结束符
}
*/
import "C"
func main() {
// C.CString Go语言string 转化为C string
// C.SayHello 上面封装的C函数SayHello
C.SayHello(C.CString("callback C.SayHello function"))
}
C语言头文件(*.h)和实现文件(.c) #
头文件chapter03/hello.h
extern void SayHello(const char* s); //注意;结束符
实现文件chapter03/hello.c
#include <stdio.h>
#include "hello.h"
void SayHello(const char* s) {
puts(s);
}
调用文件chapter03/chapter03.go
package main
//#include "hello.h"
import "C"
func main() {
C.SayHello(C.CString("build hello.h hello.c"))
}
#output
build hello.h hello.c
c语言接口的Go语言实现 #
头文件chapter04/hello.h
extern void SayHello(/*const*/ char* s); //函数参数去掉const修饰符
extern void SayHelloGo(_GoString_ s); //GoString C语言定义的go语言字符串
实现chapter04/chapter04.go
package main
//#include "hello.h"
//如果不引入*.h可以通过下面的方式引入,2种方式都可
//extern void SayHello(char* s);
//extern void SayHelloGo(_GoString_ s);
import "C"
import "fmt"
//export SayHello
func SayHello(s *C.char) {
fmt.Print(C.GoString(s))
}
//export SayHelloGo
func SayHelloGo(s string) {
fmt.Println(s)
}
func main() {
C.SayHello(C.CString("call back go=>c.SayHello"))
//参数直接就是go语言字符串,避免转换消耗
C.SayHelloGo("callback go=>c SayHellogo")
}
#output
call back go=>c.SayHellocallback go=>c SayHellogo
go语言实现的方法要加入//export SayHello注释
hello.h中定义了函数SayHello,go代码中SayHello(s *C.char)具体实现s *C.char为c语言类型
代码中C.GoString(s)把c语言的字符串转化为go语言的字符串
主函数main中,调用了C.SayHello() hello.h中实现的方法
C与Go类型转换 #
Go和C指针之间转换
数值和指针之间的转换
不同类型指针转换
字符串和切片之间转换
Go无类型指针和uintptr指针 #
var (
p unsafe.Pointer = nil //unsafe包
q uintptr = uintptr(p) //builtin包
)
C语言无类型指针和数值化指针 #
void *p = NULL;
uintptr_t q = (uintptr_t)(p); //<stdio.h>
unsafe.Pointer是Go语言和C语言指针转换的中介
uintptr 是Go语言中数值和指针转换的中介
具体介绍请查看 指针, uintptr, unsafe.Pointer
go语言unsafe包 #
type ArbitraryType int
type Pointer *ArbitraryType
//值所对应变量在内存中的大小
func Sizeof(x ArbitraryType) uintptr
//结构体中成员的偏移量(实践代码中namePointer := unsafe.Pointer(uintptr(unsafe.Pointer(&user)) + unsafe.Offsetof(user.name))
func Offsetof(x ArbitraryType) uintptr
//值所对应的变量在内存地址中的几个字节对齐
func Alignof(x ArbitraryType) uintptr
C语言版本 #
typedef void* Pointer
//值对应变量内存地址和Go基本一致
SizeOf(type of expression) //C
//宏,定义表达式和Go基本一致
Offsetof(type,member) //<stddef.h>
//C++新特性,可忽略
Alignof(type~id) //C++ 11
Go字符串和切片的结构 #
type StringHeader struct {
Data uintptr
Len int
}
type SliceHeader struct {
Data uintptr
Len int
Cap int
}
c语言cgo中定义
typedef struct{const char* p; GoInt n;} GoString;
typedef struct{void *data; GoInt len; GoInt cat;} GoSlice;
reflect包定义的结构和cgo生成的结构是一致的
GoSting和GoSlice对应的头部结构是兼容的
package main
import (
"fmt"
"reflect"
"unsafe"
)
var (
hdr reflect.StringHeader
sdr reflect.SliceHeader
str string = "abcedfa"
slice []int = []int{1, 2, 3, 4, 5, 6, 10}
)
func main() {
//字符串结构
hdr.Data = uintptr(unsafe.Pointer(&str))
hdr.Len = len(str)
//切片架构
sdr.Data = uintptr(unsafe.Pointer(&slice))
sdr.Len = *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&slice)) + uintptr(8)))
sdr.Cap = *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&slice)) + uintptr(16)))
fmt.Printf("%+v,%+v\n",hdr,sdr)
fmt.Printf("hdr output string:%s\n", *(*string)(unsafe.Pointer(hdr.Data)))
fmt.Printf("sdr output slice:%v",*(*[]int)(unsafe.Pointer(sdr.Data)))
sh := (*reflect.StringHeader)(unsafe.Pointer(&str))
//slice1和slice转换
fmt.Printf("%p,%p\n",&slice, &slice1)
ps := (*reflect.SliceHeader)(unsafe.Pointer(&slice))
ps1 := (*reflect.SliceHeader)(unsafe.Pointer(&slice1))
*ps = *ps1
fmt.Println(ps, ps1)
#output
0x203e80,0x203ea0
&{2059776 7 7} &{2059776 7 7}
}
C和GO之间调用 #
package main
/*
#include <errno.h>
static void seterrno(int err) {
errno = err;
}
static int add(int a, int b) {
return a + b;
}
*/
import "C"
import "fmt"
func main() {
v,err := C.add(12,33)
fmt.Println(v,err)
_,err := C.seterrno(9527)
fmt.Println(err)
}
任何C函数都可带2个返回值
第二返回值为errorno,对应error接口类型
seterrno自定义errno值
Go和C处理Slice #
package main
/*
#include <stdio.h>
int loop(int** list_data, int leng, char** data)
{
int* m = (int*)list_data;
int sum = 0;
for(int i=0; i<leng; i++)
{
sum += m[i];
}
*data = "finised task";
return sum;
}
*/
import "C"
import (
"fmt"
"unsafe"
)
func main() {
var ids []int32 = []int32{1,2,3,5}
var res *C.char
leng := C.int(len(ids))
le := C.loop((**C.int)(unsafe.Pointer(&ids[0])),leng ,&res)
fmt.Println(le)
fmt.Println(C.GoString(res))
fmt.Println(ids)
}
自定义slice
package main
/*
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <string.h>
typedef struct stSlice{
int *ptr;
int nLen;
int nCap;
}Slice;
Slice* imake(int nLen, int nCap){
if ((nLen < 0) || (nCap < nLen))
{
return NULL;
}
Slice *pSlice = (Slice*)malloc(sizeof(Slice));
pSlice->ptr = (int*)malloc(nCap * sizeof(int));
memset(pSlice->ptr, 0, nCap * sizeof(int));
pSlice->nLen = nLen;
pSlice->nCap = nCap;
return pSlice;
}
void resize(Slice *pSlice, int nNewLen, int nNewCap)
{
int *pOld = pSlice->ptr;
int nSize = pSlice->nLen * sizeof(int);
pSlice->ptr = (int*)malloc(nNewCap * sizeof(int));
memset(pSlice->ptr, 0, nNewCap * sizeof(int));
pSlice->nLen = nNewLen;
pSlice->nCap = nNewCap;
memcpy(pSlice->ptr, pOld, nSize);
free(pOld);
pOld = NULL;
}
void Set(Slice *pSlice, int nIndex, int nVal){
pSlice->ptr[nIndex] = nVal;
}
int Get(Slice *pSlice, int index){
return pSlice->ptr[index];
}
void Free(Slice *pSlice){
if(pSlice->ptr != NULL){
free(pSlice->ptr);
pSlice->ptr = NULL;
}
}
int len(Slice *pSlice){
return pSlice->nLen;
}
int cap(Slice *pSlice){
return pSlice->nCap;
}
*/
import "C"
import (
"fmt"
)
type Slice struct{
nLen int
nCap int
pCSlice *C.Slice // C.Slice内部保存有(C语言)对动态数组操作的指针,方便内存的释放与操作内存
}
func imake(nlen int, ncap int) *Slice{
if(nlen < 0) || (ncap < nlen){
return nil
}
cs := C.imake(C.int(nlen), C.int(ncap))
slice := new(Slice)
slice.nLen = int(cs.nLen)
slice.nCap = int(cs.nCap)
slice.pCSlice = cs
return slice
}
func iappend(slice *Slice, nums ... int) *Slice{
nSliceLen := ilen(slice)
zlen := nSliceLen + len(nums)
if zlen < icap(slice){
slice.nLen = zlen
}else{
zcap := zlen
if zcap < 2*ilen(slice){
zcap = 2*ilen(slice)
}
C.resize(slice.pCSlice, C.int(zlen), C.int(zcap))// 重新分配数组大小
slice.nLen = int(slice.pCSlice.nLen)
slice.nCap = int(slice.pCSlice.nCap)
}
index := nSliceLen
for _,val := range nums{// 将添加的数据,写入到末尾
set(slice, index, val)
index++
}
return slice
}
func ilen(slice *Slice) int{
return slice.nLen
}
func icap(slice *Slice) int{
return slice.nCap
}
func set(slice *Slice, index int, val int){
if index >= ilen(slice){
panic("error:index out of range")
}
C.Set(slice.pCSlice, C.int(index), C.int(val))
}
func get(slice *Slice, index int) int{
if index >= ilen(slice){
panic("error:index out of range")
}
nVal := C.Get(slice.pCSlice, C.int(index))
return int(nVal)
}
func Free(slice *Slice){
C.Free(slice.pCSlice)
slice.nLen = -1
slice.nCap = -1
}
func printSlice(slice *Slice){
fmt.Printf("\nslice : ")
for i := 0; i < ilen(slice); i++{
fmt.Printf("%v ", get(slice, i))
}
fmt.Printf("\nlen = %v", ilen(slice))
fmt.Printf("\ncap = %v\n", icap(slice))
}
func main(){
pSlice := imake(5,8)
for i := 0; i < ilen(pSlice); i++{
set(pSlice, i, i+2)
}
printSlice(pSlice)
pSlice = iappend(pSlice, 10,20,30)
printSlice(pSlice)
pSlice = iappend(pSlice, 4,5,6,7,8,9,0)
printSlice(pSlice)
Free(pSlice)
}
Go 和 C 语言类型转化 #
unsafe.Pointer(&d) ==> void *d = NULL
uintptr ==> uintptr_t t = (unitptr_t)(d)
int => C.int
byte => C.char
string => *C.char
slice[]int => **C.int
//C结构体
typedef struct Point {
float x;
float y;
}Point;
//在go中C.Point
func C.GoString(cString *C.char) string
func C.GoStringN(cString *C.char, length C.int) string
C.char
C.schar(signed char)
C.uchar(unsigned char)
C.short
C.ushort(unsigned short)
C.int
C.uint(unsigned int)
C.long
C.ulong(unsigned long)
C.longlong(long long)
C.ulonglong(unsigned long long)
C.float
C.double
package main
/*
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define MAX_FACES_PER_DETECT 64
typedef struct Point{
float x;
float y;
}Point;
typedef struct Rectangle{
Point lt;
Point rd;
}Rectangle;
typedef struct DetectFaceInfo{
int id;
float score;
Rectangle pos;
}DetectFaceInfo;
//void setStruct(DetectFaceInfo **ppDetectInfo) 可以这样定义
void setStruct(void **ppDetectInfo)
{
DetectFaceInfo *pDetectInfo = (DetectFaceInfo *)malloc(sizeof(DetectFaceInfo));
memset(pDetectInfo, 0 , sizeof(pDetectInfo));
pDetectInfo->id = 1;
pDetectInfo->score = 0.98f;
pDetectInfo->pos.lt.x = 1;
pDetectInfo->pos.lt.y = 1;
pDetectInfo->pos.rd.x = 9;
pDetectInfo->pos.rd.y = 10;
fprintf(stdout, "A pDetectInfo address : %p\n", pDetectInfo);
*ppDetectInfo = pDetectInfo;
}
int printStruct(void *pdetectinfo)
{
DetectFaceInfo * pDetectInfo = (DetectFaceInfo *)pdetectinfo;
fprintf(stdout, "B pDetectInfo address : %p\n", pDetectInfo);
fprintf(stdout, "id: %d\n", pDetectInfo->id);
fprintf(stdout, "score : %.3lf\n", pDetectInfo->score);
fprintf(stdout, "pos.lt.x : %d\n", pDetectInfo->pos.lt.x);
fprintf(stdout, "pos.lt.y : %d\n", pDetectInfo->pos.lt.y);
fprintf(stdout, "pos.rd.x : %d\n", pDetectInfo->pos.rd.x);
fprintf(stdout, "pos.rd.y : %d\n", pDetectInfo->pos.rd.y);
}
int freeStruct(void *pDetectInfo)
{
fprintf(stdout, "C pDetectInfo address : %p\n", pDetectInfo);
free((DetectFaceInfo*)pDetectInfo);
}
*/
import "C"
import (
_ "fmt"
_ "reflect"
"unsafe"
)
func main() {
var pDetectInfo unsafe.Pointer
C.setStruct(&pDetectInfo)
C.printStruct(pDetectInfo)
C.freeStruct(pDetectInfo)
//如果上面代码定义
//void setStruct(DetectFaceInfo **ppDetectInfo)
//go指针参数也要修改
//var pDetectInfo *C.DetectFaceInfo
//C.setStruct(&pDetectInfo)
//C.printStruct(unsafe.Pointer(pDetectInfo))
//C.freeStruct((unsafe.Pointer(pDetectInfo))
}