Golang中使用cgo或c-package机制的自定义编译器组合实践
Golang中使用cgo或c-package机制的自定义编译器组合实践 我仍然是一个编程新手。 我打算结合使用我自己的编译器和Go语言。 因此,我想了解cgo是如何工作的。 我知道cgo很难理解,但如果你能提供一个关于C包如何工作的链接或解释,我将不胜感激。 如果你能告诉我,我将非常感激 (^ _ ^ )
感谢您深入讲解了我对魔法导入概念感到模糊的地方 ^_^
更多关于Golang中使用cgo或c-package机制的自定义编译器组合实践的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
是的,如果你仔细阅读,我的问题是“使用 #include 是否会减慢进程?文章中没有澄清这部分。我(?)我是一个容易……的人。”
顺便说一下,感谢你让我知道这个问题。
为了确认我的理解,我想请你检查一下相似之处。
也就是说,使用 import "C" 会运行 C/C++ 编译器来编译 Go 代码。因此,编译会花费很长时间。
import "C"
fea_kafea:
使用
#include会减慢进程吗?
使用 cgo 会减慢编译和执行速度。编译速度变慢是因为 Go 除了编译 Go 代码外,还必须运行 C/C++ 编译器来编译非 Go 代码;执行速度变慢是因为 Go 的运行时和调用约定与 C 不兼容,因此在 Go 和 C/C++ 之间进行调用会产生开销。
也就是说,使用
import "C"会让 C/C++ 编译器来编译 Go 代码。
根据定义,只有 Go 编译器才能编译 Go 代码(也就是说,一个也能编译 Go 代码的 C/C++ 编译器,它本身就是一个 Go 编译器)。import "C" 在 Go 中是一个“魔法”导入,当 Go 编译器看到它时,Go 编译器会启动 C 编译器来编译 import "C" 上方的注释文本。我不清楚 Go 编译器具体是如何将这些注释文本提供给 C 编译器的;可能是将其复制到一个临时文件中,然后作为输入传递给 C 编译器。在 C 编译器编译完 C 代码之后,Go 编译器还需要在任何调用 C 函数的地方生成包装代码,因为 C 的调用约定和操作系统线程的使用方式与 Go 运行时执行 Go 函数和 goroutine 的方式不同且不兼容。然后,编译好的 Go 代码和 C 代码必须进行链接。根据一些谷歌搜索的结果,其中一次搜索将我带到了这里,看起来链接可能是一个耗时较多的步骤,但我不清楚具体细节。
我唯一使用过 cgo 的地方是这里,但我无法告诉你我为什么要这么做;我不记得了。无论当时是什么原因,我后来发现了如何在 Go 中不使用 cgo 来实现同样的功能,就不再使用这个了。
在Go中通过cgo调用C代码的基本方式如下:
// 文件:main.go
package main
// #include <stdio.h>
// #include <stdlib.h>
//
// void myCFunction() {
// printf("来自C函数的调用\n");
// }
import "C"
import "fmt"
func main() {
fmt.Println("Go代码开始执行")
C.myCFunction() // 调用C函数
fmt.Println("返回Go代码")
}
编译运行:
go run main.go
输出:
Go代码开始执行
来自C函数的调用
返回Go代码
更复杂的示例,包含参数传递和内存管理:
package main
/*
#include <stdlib.h>
#include <string.h>
char* processString(const char* input) {
char* output = (char*)malloc(strlen(input) + 1);
strcpy(output, input);
// 转换为大写
for(int i = 0; output[i]; i++) {
if(output[i] >= 'a' && output[i] <= 'z')
output[i] = output[i] - 32;
}
return output;
}
*/
import "C"
import (
"fmt"
"unsafe"
)
func main() {
goStr := "hello, cgo!"
// Go字符串转换为C字符串
cStr := C.CString(goStr)
defer C.free(unsafe.Pointer(cStr)) // 确保释放内存
// 调用C函数
result := C.processString(cStr)
defer C.free(unsafe.Pointer(result)) // 释放C函数分配的内存
// C字符串转换回Go字符串
goResult := C.GoString(result)
fmt.Printf("原字符串: %s\n", goStr)
fmt.Printf("处理后: %s\n", goResult)
}
使用独立的C头文件和源文件:
// 文件:main.go
package main
// #cgo CFLAGS: -I.
// #include "mylib.h"
import "C"
func main() {
C.sayHello()
result := C.addNumbers(10, 20)
println("结果:", int(result))
}
// 文件:mylib.h
#ifndef MYLIB_H
#define MYLIB_H
void sayHello();
int addNumbers(int a, int b);
#endif
// 文件:mylib.c
#include <stdio.h>
#include "mylib.h"
void sayHello() {
printf("Hello from C library\n");
}
int addNumbers(int a, int b) {
return a + b;
}
编译运行:
go build -o myapp main.go
./myapp
cgo的关键机制:
import "C"语句必须紧跟在C代码注释之后- C代码通过
C.前缀访问 - 使用
C.CString()和C.GoString()进行字符串转换 - C分配的内存必须使用
C.free()释放 #cgo指令可以指定编译标志和链接选项
这种机制允许Go直接调用C函数,使你可以将自定义编译器生成的C代码集成到Go程序中。


