项目中需要用Golang调用C生成密码,因此借机熟悉了一下cgo的一些用法。

基本用法网上已经有教程,但是关于字符串的传递的文章还不多,因此记录下。

1. 编写C文件,包含一个函数,函数接受字符串,同时返回字符数组。注意这里的返回的数组是malloc申请的,调用的时候需要释放。

//foo.c
#include <stdio.h>
#include <stdlib.h>
#include "foo.h"

#define LENGTH 16
char* foo(char *input) {
    char* m = malloc(LENGTH*sizeof(char));
    sprintf(m, "%s", input);
    return m;
}

以下是相应头文件

//foo.h
#ifndef _FOO_H_
#define _FOO_H_
char* foo(char* input);
#endif

2. 编译成动态链接库

gcc -c -fpic foo.c

gcc -shared foo.o -o foo.so

3. golang调用C代码

package main

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

func main() {
	cs := C.CString("hello")
	result := C.foo(cs)
	str := C.GoString(result)
	C.free(unsafe.Pointer(cs))
	C.free(unsafe.Pointer(result))
	fmt.Println(str)
}

注意:

  • 由于将动态链接文件和go代码放在同一目录下了,所以在golang中就不需要再通过cgo指定链接的路径和文件名了
  • 传递参数的时候需要将golang中的string和c类型的char *作类型转换,转换方法通过C.CString来实现
  • 调用C函数之后的返回值类型是C类型的char*, 需要通过C.GoString来作类型转换。此函数会将原来的内容拷贝一份,因此即使先free掉result,str变量的使用依然不受影响
  • 一定记得调用C.free释放指针指向的内存,防止内存泄漏