运行.SetFinalizer:无法确定 C.Char 的名称类型



请考虑以下示例 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消息之前返回,它就会丢失。 在实际程序中,您不需要这个。

最新更新