cgo编程知识总结

要安装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))
}