golang开发之cgo第二节:C提供动态库在go中使用

文章介绍了如何在Go中内嵌和调用C代码,包括使用`cgo`规则,转换数据类型,调用C函数,以及处理C语言结构体。此外,还展示了如何调用C源文件中的函数和C语言动态库。最后,提到了Go调用C++函数的例子,通过引入C++头文件并在Go中引用。
摘要由CSDN通过智能技术生成

第一节、 go中内嵌原生c代码

  • 编译命令:go build -o 生成后文件名 main.go;
    – 规则一:调用C函数时,必须是C.开头;
    – 规则二:调用C函数时,参数也需要转为C类型;

  • 注意:
    – CGO 会保留序文中的宏定义,但是并不会保留注释,也不支持#program,C 代码块中的#program 语句极可能产生未知错误;
    – CGO 中使用 #cgo 关键字可以设置编译阶段和链接阶段的相关参数,可以使用 ${SRCDIR} 来表示 Go 包当前目录的绝对路径。
    – 使用 C.结构名 或 C.struct_结构名 可以在 Go 代码段中定义 C 对象,并通过成员名访问结构体成员。

  • 示例一

package main

/*
#include <stdio.h>

static void SayHello(const char* s) {
    puts(s);
}

void myprintf(const char* s) {
	printf("%s\n", s);
}
*/
import "C"

func main() {
	C.SayHello(C.CString("Hello, World"))
	C.myprintf(C.CString("haha"))
}
  • 示例二
// test3.go
package main

/*
#cgo LDFLAGS: -L/usr/local/lib

#include <stdio.h>
#include <stdlib.h>
#define REPEAT_LIMIT 3              // CGO会保留C代码块中的宏定义
typedef struct{                     // 自定义结构体
    int repeat_time;
    char* str;
}blob;
int SayHello(blob* pblob) {  // 自定义函数
    for ( ;pblob->repeat_time < REPEAT_LIMIT; pblob->repeat_time++){
        puts(pblob->str);
    }
    return 0;
}
*/
import "C"
import (
    "fmt"
    "unsafe"
)

func main() {
    cblob := C.blob{}                               // 在GO程序中创建的C对象,存储在Go的内存空间
    cblob.repeat_time = 0

    cblob.str = C.CString("Hello, World\n")         // C.CString 会在C的内存空间申请一个C语言字符串对象,再将Go字符串拷贝到C字符串

    ret := C.SayHello(&cblob)                       // &cblob 取C语言对象cblob的地址

    fmt.Println("ret", ret)
    fmt.Println("repeat_time", cblob.repeat_time)

    C.free(unsafe.Pointer(cblob.str))               // C.CString 申请的C空间内存不会自动释放,需要显示调用C中的free释放
}
  • 示例二中使用 C.CString 将 Go 字符串对象转化为 C 字符串对象,并将其传入 C 程序空间进行使用,由于 C 的内存空间不受 Go 的 GC 管理,因此需要显示的调用 C 语言的 free 来进行回收。

第二节、 go调用 C 源文件中的函数

  • 在 Go 代码中,声明 SayHello() 函数,再引用 hello.c 源文件,就可以调起外部 C 源文件中的函数了。
// demo/hello.c
#include <stdio.h>
int SayHello() {
 puts("Hello World");
    return 0;
}
// demo/main.go
package main
/*
#include "hello.c"
int SayHello();
*/
import "C"
import (
    "fmt"
)

func main() {
    ret := C.SayHello()
    fmt.Println(ret)
}

第三节、 go调用 C 语言so动态库

  • 第一步:创建C语言动态库,源码如下
  • 第二步:生成动态库 gcc -fPIC -shared -o libtest.so test.c
  • 第三步:将动态库生效,否则会找不到该so库,命令如下:
    ---------- export LD_LIBRARY_PATH=./
    ---------- 备注:路径为动态库所在路径
// test.c
#include <stdio.h>

int add(int a, int b) {
    return a+b;
}
// test.h
int add(int a, int b);
  • 第一步:导入C动态库头文件;
  • 第二步:导入动态库文件;
  • 第三步:编译命令 go build -o main main.go;
    ---------- 也可以使用-x查看编译过程 go build -x -o main main.go;
  • 扩展:也可以通过go tool cgo main.go生成能够调用 C 语言代码的 Go 语言源文件,也就是说所有启用了 CGO 特性的 Go 代码,都会首先经过 cgo 的"预处理"。
// main.go
package main

/*
#cgo CFLAGS: -I ./test.h
#cgo LDFLAGS: -L./ -ltest
#include "test.h"
*/
import "C"

import (
	"fmt"
)

func main() {
	d := C.add(C.int(10), C.int(20))
	fmt.Println(d)
}
在如上例子中,库文件是编译成动态库的,main程序链接的时候也是采用的动态库,可通过ldd main查看。

第四节、 Go 调用 C++

// demo/hello.h
int SayHello();
  • 使用 C++来重新实现这个 C 函数
// demo/hello.cpp
#include <iostream>

extern "C" {
    #include "hello.h"
}

int SayHello() {
 std::cout<<"Hello World";
    return 0;
}
  • 最后再在 Go 代码中,引用 hello.h 头文件,就可以调用 C++实现的 SayHello 函数了
// demo/test6.go
package main
/*
#include "hello.h"
*/
import "C"
import (
    "fmt"
)

func main() {
    ret := C.SayHello()
    fmt.Println(ret)
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

尘帝阁

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值