如何将 **C.char 数据传回调用者的缓冲区?
How do I pass **C.char data back to caller's buffer?
我将构建一个 Go 共享对象二进制文件(.DLL 和 .so),将字符串传回 Java。为了计算从 Go 传递的 C 字符串,我写了这个:
package main
/*
#include <stdlib.h>
*/
import "C"
import (
"log"
"unsafe"
)
//export passBackHello
func passBackHello(buf **C.char) C.int {
str := "Hello World!"
length := len(str)
cString := C.CString(str) // returns *C.char
defer C.free(unsafe.Pointer(cString))
log.Println("In passBackHello: cString:", C.GoStringN(cString, C.int(length)))
*buf = C.CString(str) // works
*buf = cString // doesn't work
log.Println("In passBackHello: buf:", C.GoStringN(*buf, C.int(length)))
return C.int(length)
}
func main() {
buf := make([]byte, 8192) //create my buffer
cStrPointer := (**C.char)(unsafe.Pointer(&buf[0]))
defer C.free(unsafe.Pointer(cStrPointer))
lengthCint := passBackHello(cStrPointer)
log.Println("In main: length:", int(lengthCint))
log.Println("In main: buf:", C.GoStringN(*cStrPointer, lengthCint))
log.Println("In main: buf:", C.GoString(*cStrPointer))
}
当我在函数 passBackHello
中使用 *buf = C.CString(str)
时,它起作用了:
2018/03/31 19:33:54 In passBackHello: cString: Hello World!
2018/03/31 19:33:54 In passBackHello: buf: Hello World!
2018/03/31 19:33:54 In main: length: 12
2018/03/31 19:33:54 In main: buf: Hello World!
2018/03/31 19:33:54 In main: buf: Hello World!
exit status 3221226356
当我在函数 passBackHello
中使用 *buf = cStringPointer
时,它在 buf
中显示,而在 passBackHello
中显示,但在 main
中不显示:
2018/03/31 19:33:05 In passBackHello: cString: Hello World!
2018/03/31 19:33:05 In passBackHello: buf: Hello World!
2018/03/31 19:33:05 In main: length: 12
2018/03/31 19:33:05 In main: buf: ⌂3 X☺�
2018/03/31 19:33:05 In main: buf: ⌂3
exit status 3221226356
我需要让 *buf = cStringPointer
版本工作,因为那个版本有 C 字符串变量的 C.free
。我是运行go版本go1.10.1windows/amd64.
更新
在应用 Azeem 的答案和一些其他清理后,工作的 Go 代码如下所示:
//export passBackHello
func passBackHello(cStrPointer **C.char) C.int {
str := "Hello World!"
length := len(str)
*cStrPointer = C.CString(str) // copies *C.char into caller's buffer
log.Println("In passBackHello *cStrPointer:", C.GoStringN(*cStrPointer, C.int(length)))
return C.int(length)
}
func main() {
buf := make([]byte, 8192) //create my buffer
cStrPointer := (**C.char)(unsafe.Pointer(&buf[0]))
defer C.free(unsafe.Pointer(cStrPointer))
lengthCint := passBackHello(cStrPointer)
log.Println("In main: length:", int(lengthCint))
log.Println("In main: *cStrPointer:", C.GoStringN(*cStrPointer, lengthCint))
log.Println("In main: *cStrPointer:", C.GoString(*cStrPointer))
}
为了完整起见,Java 调用者看起来像这样:
// allocate a void**
final PointerByReference decCStringPointer = new PointerByReference();
// call the C function
Integer decLength = gpg.passBackHello(decCStringPointer);
// extract the void* that was allocated in C
final Pointer p = decCStringPointer.getValue();
// extract the null-terminated string from the Pointer
final String decValue = p.getString(0);
System.out.printf("decrypted length: %d\n", decLength);
System.out.printf("decrypted value: %s\n", decValue);
看起来缓冲区在C内存中。如何从 Java 中释放它?
根据C.CString
函数的文档:
// 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
您不需要在您的函数中释放它。应该释放它的是调用者(即 main
函数)。
从您的 passBackHello
中删除 defer C.free(unsafe.Pointer(cString))
行,它应该可以工作!
注意:
您不需要那个缓冲区(即 buf
),因为它是正在返回的指针并且已经分配了内存。如果需要,您可以将其复制到缓冲区中并释放指针。
我将构建一个 Go 共享对象二进制文件(.DLL 和 .so),将字符串传回 Java。为了计算从 Go 传递的 C 字符串,我写了这个:
package main
/*
#include <stdlib.h>
*/
import "C"
import (
"log"
"unsafe"
)
//export passBackHello
func passBackHello(buf **C.char) C.int {
str := "Hello World!"
length := len(str)
cString := C.CString(str) // returns *C.char
defer C.free(unsafe.Pointer(cString))
log.Println("In passBackHello: cString:", C.GoStringN(cString, C.int(length)))
*buf = C.CString(str) // works
*buf = cString // doesn't work
log.Println("In passBackHello: buf:", C.GoStringN(*buf, C.int(length)))
return C.int(length)
}
func main() {
buf := make([]byte, 8192) //create my buffer
cStrPointer := (**C.char)(unsafe.Pointer(&buf[0]))
defer C.free(unsafe.Pointer(cStrPointer))
lengthCint := passBackHello(cStrPointer)
log.Println("In main: length:", int(lengthCint))
log.Println("In main: buf:", C.GoStringN(*cStrPointer, lengthCint))
log.Println("In main: buf:", C.GoString(*cStrPointer))
}
当我在函数 passBackHello
中使用 *buf = C.CString(str)
时,它起作用了:
2018/03/31 19:33:54 In passBackHello: cString: Hello World!
2018/03/31 19:33:54 In passBackHello: buf: Hello World!
2018/03/31 19:33:54 In main: length: 12
2018/03/31 19:33:54 In main: buf: Hello World!
2018/03/31 19:33:54 In main: buf: Hello World!
exit status 3221226356
当我在函数 passBackHello
中使用 *buf = cStringPointer
时,它在 buf
中显示,而在 passBackHello
中显示,但在 main
中不显示:
2018/03/31 19:33:05 In passBackHello: cString: Hello World!
2018/03/31 19:33:05 In passBackHello: buf: Hello World!
2018/03/31 19:33:05 In main: length: 12
2018/03/31 19:33:05 In main: buf: ⌂3 X☺�
2018/03/31 19:33:05 In main: buf: ⌂3
exit status 3221226356
我需要让 *buf = cStringPointer
版本工作,因为那个版本有 C 字符串变量的 C.free
。我是运行go版本go1.10.1windows/amd64.
更新
在应用 Azeem 的答案和一些其他清理后,工作的 Go 代码如下所示:
//export passBackHello
func passBackHello(cStrPointer **C.char) C.int {
str := "Hello World!"
length := len(str)
*cStrPointer = C.CString(str) // copies *C.char into caller's buffer
log.Println("In passBackHello *cStrPointer:", C.GoStringN(*cStrPointer, C.int(length)))
return C.int(length)
}
func main() {
buf := make([]byte, 8192) //create my buffer
cStrPointer := (**C.char)(unsafe.Pointer(&buf[0]))
defer C.free(unsafe.Pointer(cStrPointer))
lengthCint := passBackHello(cStrPointer)
log.Println("In main: length:", int(lengthCint))
log.Println("In main: *cStrPointer:", C.GoStringN(*cStrPointer, lengthCint))
log.Println("In main: *cStrPointer:", C.GoString(*cStrPointer))
}
为了完整起见,Java 调用者看起来像这样:
// allocate a void**
final PointerByReference decCStringPointer = new PointerByReference();
// call the C function
Integer decLength = gpg.passBackHello(decCStringPointer);
// extract the void* that was allocated in C
final Pointer p = decCStringPointer.getValue();
// extract the null-terminated string from the Pointer
final String decValue = p.getString(0);
System.out.printf("decrypted length: %d\n", decLength);
System.out.printf("decrypted value: %s\n", decValue);
看起来缓冲区在C内存中。如何从 Java 中释放它?
根据C.CString
函数的文档:
// 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
您不需要在您的函数中释放它。应该释放它的是调用者(即 main
函数)。
从您的 passBackHello
中删除 defer C.free(unsafe.Pointer(cString))
行,它应该可以工作!
注意:
您不需要那个缓冲区(即 buf
),因为它是正在返回的指针并且已经分配了内存。如果需要,您可以将其复制到缓冲区中并释放指针。