请考虑下面的示例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: %T\n", 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。当我试图使用cString释放变量runtime.SetFinalizer时,我遇到了以下情况:
$ go build a.go
# command-line-arguments
./a.go:22:41: could not determine kind of name for C.Char请给我指出正确的方向。谢谢!
发布于 2020-06-20 22:06:59
当cgo系统将包装器转换为Go编译器所理解的内容时,它必须将每个C类型转换为Go类型,以满足不同的目的。事实证明,这不适用于您的情况(这是您看到的错误)。
这实际上是可以的,因为您的代码从一开始就不会以您想要的方式工作。当Go的垃圾收集器准备释放占用Go内存的Go对象时,运行时终结器将运行,但是C.Cstring返回一个不是Go内存的指针。特别是,请注意以下来自cgo文档的引号
// Go字符串到C字符串// C字符串使用malloc在C堆中分配。//调用方有责任安排它被//释放,例如调用C.free (确保包括stdlib.h //如果需要C.free )。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: %T\n", 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中强制使用了一个GC。这将触发终结器:
$ ./cfree_example
C.CString type: *main._Ctype_char
freeing C string“丑陋的黑客”就在其中,因为如果main在终结器调用编写完freeing C string消息之前返回,它就会丢失。在一个真正的程序中,您将不需要这个。
https://stackoverflow.com/questions/62482446
复制相似问题