None
None

Reputation: 2433

How to copy *C.char?

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)

Upvotes: 4

Views: 208

Answers (1)

kostix
kostix

Reputation: 55553

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.

Upvotes: 1

Related Questions