请考虑以下示例 go 代码:
package main
/*
#include <stdio.h>
#include <stdlib.h>
*/
import "C"
import (
"fmt"
"runtime"
"unsafe"
)
func main() {
// Convert Go string to C string using C.CString
cString := C.CString("Wold!")
fmt.Printf("C.CString type: %Tn", cString)
//C.free(unsafe.Pointer(cString)) // <-- this works, but I don't want to free it manually..
runtime.SetFinalizer(&cString, func(t *C.Char) {
C.free(unsafe.Pointer(t))
})
}
我正在尝试cGo,并试图释放cString
。当我尝试使用runtime.SetFinalizer
释放变量 cString 时,我遇到了:
$ go build a.go
# command-line-arguments
./a.go:22:41: could not determine kind of name for C.Char
请指出正确的方向。谢谢!
当 cgo 系统将你的包装器变成 Go 编译器理解的东西时,它必须出于各种目的将每个 C 类型转换为 Go 类型。 事实证明,这不适用于您的情况(这是您看到的错误(。
这实际上没关系,因为你的代码一开始就永远不会按照你想要的方式工作。 当 Go 的垃圾回收器准备释放占用 Go 内存的 Go 对象时,运行时终结器运行,但C.Cstring
返回的指针不是 Go 内存。 特别是,请注意 cgo 文档中的以下引文:
// Go string to C string // The C string is allocated in the C heap using malloc. // It is the caller's responsibility to arrange for it to be // freed, such as by calling C.free (be sure to include stdlib.h // if C.free is needed). func C.CString(string) *C.char
由于返回的字符串位于"C 堆"上,因此 Go 垃圾回收器永远不会最终确定它。 如果你的代码被编译了,那将只是一个无操作。
如果你有一个 Go 对象,其生命周期与 C 对象的生存期相似,你也许可以使用它。 这是一个虚构(但有效(的示例:
package main
/*
#include <stdio.h>
#include <stdlib.h>
*/
import "C"
import (
"fmt"
"runtime"
"time"
"unsafe"
)
type S struct {
Foo int
ToFree unsafe.Pointer
}
func main() {
doit()
runtime.GC()
time.Sleep(10 * time.Millisecond) // ugly hack
}
func doit() {
cString := C.CString("Wold!")
fmt.Printf("C.CString type: %Tn", cString)
x := &S{Foo: 1, ToFree: unsafe.Pointer(cString)}
runtime.SetFinalizer(x, func(t *S) {
fmt.Println("freeing C string")
C.free(t.ToFree)
})
}
当为x
分配的对象超出范围时,它将符合 GC 的条件。 实际的 GC 可能永远不会发生,所以我在main
中用runtime.GC()
强制了一个。 这将触发终结器:
$ ./cfree_example
C.CString type: *main._Ctype_char
freeing C string
"丑陋的黑客"就在那里,因为如果main
在终结器调用完成写入freeing C string
消息之前返回,它就会丢失。 在实际程序中,您不需要这个。