What's the equivalent of mempcy() for *C.char?
I have a function that will be called from C using -buildmode=c-shared
:
myGoStr := "blabla"
//export GetString
func GetString(text *C.char) {
memcpy(text, myGoStr)
}
How can I copy the Go string to the C allocated memory behind *C.char
?
I'm well aware of all the C.String functions (https://pkg.go.dev/cmd/cgo#hdr-Go_references_to_C) but I don't want Go to allocate the memory, I want to work directly with a C pointer given by the C application.
The only way I found is:
cStr := C.CString(text)
C.strcpy(text, cStr)
C.free(unsafe.Pointer(cStr))
but it's too overkill since it involves 2 copies (C.CString
and C.strcpy
)
There are multiple ways to achieve what you want but the thing is, either way will be unsafe — in one way or another. The reason for this is that memcpy
is already unsafe (in the Go's frame of thinking) because its third argument — the number of bytes to copy — is not somehow obtained from the rest of that function's arguments (what the Go's copy
does).
So yes, you can avoid the usual Go's unsafe
dance to construct a slice out of your *C.char
to pass to copy
and have that warm cozy feeling of being safe, but then another solution — such as just calling memcpy
directly — will anyway rely on the said *C.char
to point to a memory block with enough free space to contain at least as much bytes there is in the Go's source string, plus one, and nothing guarantees that.
I mean, if you want to go this way, you can just do that:
/*
#include <string.h>
static void memcpy_from_go(char *dst, _GoString_ src)
{
memcpy(dst, src.p, src.n);
}
*/
import "C"
const myGoStr = "blabla"
//export GetString
func GetString(text *C.char) {
C.memcpy_from_go(text, myGoStr)
// (!) note that no NUL terminator is placed into the destination block
}
func main() {}
But as there's no safeness anyway, I would argue that the analogous
func strcpyToC(dst *C.char, src string) {
n := len(src)
ds := unsafe.Slice((*byte)(unsafe.Pointer(dst)), n+1)
copy(ds, src)
ds[n] = 0
}
//export GetString
func GetString(text *C.char) {
strcpyToC(text, myGoStr)
}
is just clearer and and just as unsafe.
To reiterate in other words, you cannot do memory operations with the C side from your Go side and still claim your Go side is safe.