CGO 是什么
CGO 让 Go 程序直接调用 C/C++ 代码——复用成熟 C 库、对接底层系统能力、性能敏感路径用 C 实现。代价是引入 cgo 调用开销、跨语言内存管理复杂度、编译依赖(需要 C 编译器)。
基础语法
/*
// C 或 C++ 代码
#include <stdio.h>
*/
import "C" // 必须紧跟注释块,中间不能有空行
func main() {
C.puts(C.CString("Hello from CGO!"))
}
三个要点:注释块里的内容交给 cgo 预处理;import "C" 必须紧跟注释块;通过 C. 前缀访问 C 函数和类型。
类型映射

| Go 类型 | C 类型 |
|---|---|
| C.char / C.int / C.float | char / int / float |
| *C.char | char* |
| unsafe.Pointer | void* |
调用 C++:extern “C” 包装
C++ 的函数名会被修饰、还有异常机制,不能直接跨 cgo 边界。标准做法是加一层 extern "C" 包装:
// mylib.h
#ifdef __cplusplus
extern "C" {
#endif
void cppFunction();
#ifdef __cplusplus
}
#endif
/*
#cgo CXXFLAGS: -std=c++11
#cgo LDFLAGS: -L. -lmylib
#include "mylib.h"
*/
import "C"
内存管理原则
跨语言内存的铁律:谁分配谁释放。Go 侧创建的 C 字符串必须显式 free:

/*
#include <stdlib.h>
*/
import "C"
import "unsafe"
func main() {
cstr := C.CString("Hello")
defer C.free(unsafe.Pointer(cstr)) // 必须释放,否则泄漏
C.puts(cstr)
}
实战:Dijkstra 集成(C++ 实现 + Go 调用)
C++ 侧
// dijkstra.h
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
int* path;
int length;
int total_cost;
} PathResult;
PathResult* dijkstra(int graph[], int vertices, int start, int end);
void free_path_result(PathResult* result);
#ifdef __cplusplus
}
#endif
// dijkstra.cpp(核心逻辑)
#include "dijkstra.h"
#include <vector> <queue> <climits> <algorithm>
using namespace std;
PathResult* dijkstra(int graph[], int vertices, int start, int end) {
vector<int> dist(vertices, INT_MAX), prev(vertices, -1);
vector<bool> visited(vertices, false);
auto w = [&](int u, int v) { return graph[u * vertices + v]; };
priority_queue<pair<int,int>, vector<pair<int,int>>, greater<>> pq;
pq.push({0, start}); dist[start] = 0;
while (!pq.empty()) {
auto [d, u] = pq.top(); pq.pop();
if (visited[u]) continue;
visited[u] = true;
for (int v = 0; v < vertices; ++v) {
int weight = w(u, v);
if (weight > 0 && d + weight < dist[v]) {
dist[v] = d + weight;
prev[v] = u;
pq.push({dist[v], v});
}
}
}
// 回溯路径 → C 数组,封装进 PathResult
vector<int> path;
for (int at = end; at != -1; at = prev[at]) path.push_back(at);
reverse(path.begin(), path.end());
PathResult* r = new PathResult();
r->length = path.size();
r->path = new int[path.size()];
copy(path.begin(), path.end(), r->path);
r->total_cost = dist[end];
return r;
}
void free_path_result(PathResult* r) {
if (r) { delete[] r->path; delete r; }
}
Go 侧
package main
/*
#cgo CXXFLAGS: -std=c++11
#cgo LDFLAGS: -L. -ldijkstra
#include "dijkstra.h"
*/
import "C"
import ("fmt"; "unsafe")
func main() {
// 邻接矩阵,用 int32 保证与 C.int 大小一致
graph := []int32{
0, 10, 0, 30, 100,
0, 0, 50, 0, 0,
0, 0, 0, 0, 10,
0, 0, 20, 0, 60,
0, 0, 0, 0, 0,
}
result := C.dijkstra(
(*C.int)(unsafe.Pointer(&graph[0])), // 切片 → C 数组指针
C.int(5), C.int(0), C.int(4),
)
defer C.free_path_result(result) // C 侧分配,C 侧释放
// unsafe 把 C 数组转回 Go 切片
path := unsafe.Slice((*C.int)(unsafe.Pointer(result.path)), result.length)
goPath := make([]int, len(path))
for i := range path { goPath[i] = int(path[i]) }
fmt.Println("Path:", goPath)
fmt.Println("Total cost:", result.total_cost)
}
跨平台编译

# Linux/macOS
g++ -std=c++11 -fPIC -shared dijkstra.cpp -o libdijkstra.so
CGO_CXXFLAGS="-std=c++11" go build
# Windows
g++ -std=c++11 -shared dijkstra.cpp -o dijkstra.dll -Wl,--out-implib,libdijkstra.a
set CGO_CXXFLAGS=-std=c++11
go build
性能优化
- 减少跨语言调用次数:批量传数据,别逐元素调 C(cgo 调用开销几十 ns/次,批量能摊薄);
- 用
static inlineC 函数减少调用开销; - 大块内存分配在 C 侧用内存池复用。
常见问题
| 报错 | 原因与解决 |
|---|---|
| cannot convert *int to *_Ctype_int | 类型不匹配:Go 切片用 int32/int64 显式对齐 C 的 int(平台相关) |
| fatal error: vector: No such file or directory | C++ 头文件找不到:加 #cgo CXXFLAGS: -std=c++11 并确认编译器是 g++ |
| undefined reference to ‘dijkstra’ | 链接问题:确认函数是 extern “C”、库路径正确(-L. -ldijkstra)、符号签名一致 |
安全注意
- 指针安全:C 侧不能长期持有 Go 指针(Go 的 GC 会移动对象);跨 cgo 边界传递的 Go 指针要遵守 cgo 指针规则;
- 边界检查:C 数组访问不做越界检查,Go 侧用
unsafe.Slice时长度必须可信; - 错误处理:C 函数返回 -1/errno 时映射为 Go 的 error。

