Go 中嵌入 C 代码需用 / / 注释块置于文件顶部、import "C" 前且无空行;字符串传参须用 C.CString() + C.free();结构体/数组操作须严格遵循 C 布局与指针规则;链接失败需检查 #cgo 指令及环境变量 CGO_ENABLED。
怎么在 Go 文件里写 C 代码
Go 用
cgo
做 C 互操作,不是“调用”而是“嵌入”——C 代码必须直接写在 Go 源文件顶部的注释块里,且紧挨着
import "C"
前。漏掉空行、放错位置或用了普通注释(
//
)都会让
cgo
完全忽略那段 C 代码。
常见错误现象:
undefined: C.some_func
,其实不是函数没声明,是 C 代码压根没被 cgo 解析到。
必须用
/* */
包裹 C 代码,不能用
//
C 代码块和
import "C"
之间不能有空行,也不能有 Go 语句
如果要 include 系统头文件(如
),得写在 C 注释块里,不是 Go 的
import
import "C"
是 magic line,必须单独一行,前面不能有变量声明或注释
/*
#include
char* greet() {
return "Hello from C";
}
*/
import "C"
func main() {
s := C.CString("hi")
defer C.free(unsafe.Pointer(s))
}
字符串传参为什么总崩
C 字符串是
char*
,Go 字符串是只读、带长度的结构体。直接传
string
给 C 函数会 panic 或读到乱码——因为 Go 不保证底层字节数组地址长期有效,且没有结尾
\0
。
正确做法永远是:用
C.CString()
转成 C 可用内存,用完后显式
C.free()
。漏掉
free
就是 C 内存泄漏;提前
free
后再访问就是 use-after-free。
立即学习
“
go语言免费学习笔记(深入)
”;
C.CString(s)
返回
*C.char
,分配新内存并复制内容,自动加
\0
必须配对使用
C.free(unsafe.Pointer(ptr))
,不能用 Go 的
free
或其他方式
如果 C 函数返回
char*
且指向静态内存(比如常量字符串),可直接转
Go string
:
C.GoString(cstr)
如果返回的是堆分配内存(比如
malloc
出来的),Go 层要自己
C.free
,别指望 C 函数帮你释放
struct 和数组怎么对齐
C struct 在 Go 里对应
C.struct_xxx
,但字段顺序、填充、对齐完全按 C 编译器规则来,不是 Go 的。跨平台时尤其危险:比如
int
在 C 里可能是 32 位或 64 位,
long
更是因平台而异。
go语言参考手册 中文CHM版
Go 是一个开源的编程语言,它能让构造简单、可靠且高效的软件变得容易。本文给大家带来Go参考手册,需要的可以来下载! Go是从2007年末由Robert Griesemer, Rob Pike, Ken Thompson主持开发,后来还加入了Ian Lance Taylor, Russ Cox等人,并最终于2009年11月开源,在2012年早些时候发布了Go 1稳定版本。现在Go的开发已经是完全开放的,并且拥有一个活跃的社区。 Go 语言特色 简洁、快速、安全 并行、有趣、开源 内存管理、v数组安全、编译
下载
不要手写 Go struct 去“模拟”C struct;也不要用
unsafe.Sizeof
算偏移——它反映的是 Go 的布局,不是 C 的。
一律用
C.struct_name
,字段名和类型由 cgo 自动生成
数组传参要传指针:
&cArray[0]
,不能直接传
cArray
(Go 不允许取数组变量地址)
如果 C 函数要修改 struct 字段,必须传
*C.struct_name
,不能传值
涉及大小敏感的操作(比如
memcpy
),用
unsafe.Sizeof(C.struct_name{})
,不是
reflect.TypeOf
或手动算
编译失败提示 “undefined reference to XXX” 怎么查
这个错误不是 Go 报的,是链接器(
ld
)报的,说明 C 符号存在,但最终没被链接进来。最常见原因是:C 函数定义在外部 .c 文件里,但没告诉 cgo 哪儿找。
cgo 默认只处理 Go 文件里的 C 代码块。如果 C 实现在独立 .c 文件中,必须通过
// #cgo LDFLAGS:
或
// #cgo CFLAGS:
显式指定路径和依赖。
外部 C 文件需放在同一目录,或用
// #cgo CFLAGS: -I/path/to/headers
链接动态库:
// #cgo LDFLAGS: -L/path/to/lib -lmylib
链接静态库:确保
.a
文件路径正确,且架构匹配(比如 macOS 上
.dylib
和
.a
不能混用)
检查是否漏了
extern "C"
(C++ 场景下),否则 C++ name mangling 会导致符号找不到
容易被忽略的地方:cgo 的
#cgo
指令只对紧邻的 Go 文件生效,不能跨文件继承;
CGO_ENABLED=0
时所有 cgo 都被禁用,连语法检查都不会做——出错时先确认环境变量。