在Go中调用Golang c-shared库(.h/.so)报错,求解决方案
问题根源
你遇到的错误本质是Go导出给C的函数使用了C不兼容的类型。如果最初你的query函数参数用的是Go原生string类型,Go在生成sdk.h时,会把函数声明写成包含GoString的形式——而GoString是Go内部定义的结构体,C编译器完全不认识这个类型,直接报语法错误,进而导致cgo无法识别C.query这个符号。
当你把参数改成*C.char后,生成的头文件里函数声明会变成C兼容的extern char* query(char* info);,C编译器能正确解析,调用自然就正常了。
正确的生成与调用流程
1. 编写生成c-shared库的Go代码
必须确保导出函数的参数和返回值都是C语言能识别的类型(比如*C.char、C.int、C.float等),不能用Go原生类型(string、slice、map这些)。你的修正后的代码是正确的,要点如下:
package main //#include <stdio.h> //#include <stdlib.h> //#include <string.h> import "C" // 必须保留空main函数,否则生成库时会报错 func main() {} //export query // 参数和返回值都使用*C.char这种C兼容类型 func query(info *C.char) *C.char { goStr := C.GoString(info) // C.CString会在C堆上分配内存,调用方需要负责释放 return C.CString(goStr) }
2. 生成c-shared库
执行以下命令生成动态库和对应头文件:
go build -buildmode=c-shared -o sdk.so sdk.go
执行后会得到两个文件:sdk.so(动态库)和sdk.h(头文件),将它们放到调用方可访问的路径(比如你示例中的../lib目录)。
3. 编写调用库的Go代码
调用时需注意路径配置、内存管理等细节,修正后的代码如下:
package main /* #cgo LDFLAGS: -L../lib -lsdk #include "sdk.h" // 引入stdlib.h用于free操作 #include <stdlib.h> */ import "C" import "fmt" import "unsafe" func main() { // 将Go字符串转为C字符指针,内存分配在C堆上 cInput := C.CString("123") // 手动释放内存,避免泄漏 defer C.free(unsafe.Pointer(cInput)) // 调用库中的query函数 cResult := C.query(cInput) // 释放返回的C字符指针 defer C.free(unsafe.Pointer(cResult)) // 将C字符串转回Go字符串 goResult := C.GoString(cResult) fmt.Println(goResult) }
关键注意事项
- 类型兼容性:Go导出给C的函数,所有参数和返回值必须是C兼容的基础类型或指针,绝对不能用Go的复杂类型,否则生成的头文件会包含C无法解析的类型定义。
- 内存管理:
C.CString和C.CBytes分配的内存属于C堆,Go的垃圾回收器不会管理这部分内存,必须手动调用C.free释放,否则会造成内存泄漏。 - 库路径:
#cgo LDFLAGS中的-L指定库所在目录,-l指定库名(去掉前缀lib和后缀.so,比如sdk.so对应-lsdk)。如果运行时找不到库,需要设置LD_LIBRARY_PATH环境变量,将库所在目录加入其中。
内容的提问来源于stack exchange,提问作者Pandy Yang
相关产品推荐
相关产品推荐

