CGO: Go string -> void*/length

279 views
Skip to first unread message

Peter Mogensen

unread,
Oct 31, 2016, 10:23:40 AM10/31/16
to golang-nuts
Hi,

I was trying to find the official way to pass a Go string to CGO code as
a void* (or char*) with explicit length.
... using only one allocation/copy.

I could probably just call C.CString(string) and ignore the extra \0
added by CGO, but I'm a little puzzled that there's not a shorthand for
those with C APIs not using null-terminated strings.

Someone proposed this hack:

*((**byte)(unsafe.Pointer(&theString)))

... resulting in a *byte pointing to the data - which can then be copied
to C-managed memory.

But that relies on knowing that the string implementation starts with a
pointer to the data.

It would be great with a

func C.CStringData(string) (*C.char, C.int) // or void*

/Peter


Pietro Gagliardi

unread,
Oct 31, 2016, 10:40:10 AM10/31/16
to Peter Mogensen, golang-nuts
Just pass the null-terminated string and use C.int(len(goString)) as the length. The length of a Go string is already in bytes and does not include the terminating null (since Go has none), and I assume C.CString() produces the same byte sequence without encoding conversions.
> --
> You received this message because you are subscribed to the Google Groups "golang-nuts" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.
>

Peter Mogensen

unread,
Oct 31, 2016, 10:55:50 AM10/31/16
to golan...@googlegroups.com


On 2016-10-31 15:39, Pietro Gagliardi wrote:
> Just pass the null-terminated string and use C.int(len(goString)) as the length.

Yes... I mentioned that as a somewhat solution. ... it still allocates 1
byte more than needed.

/Peter

James Bardin

unread,
Oct 31, 2016, 12:32:35 PM10/31/16
to golang-nuts, a...@one.com

C.CBytes does essentially this, but you either need to start with a []byte, or the string conversion could be a second copy.

I don't see a need for a separate version CString without the null when you can just pass the length-1. In the rare case that the extra byte makes a difference, you could just define your own version

func CStringNoNull(s string) unsafe.Pointer {
p := C.malloc(uintptr(len(s)))
pp := (*[1<<30]byte)(p)
copy(pp[:], s)
return p
}

Florian Weimer

unread,
Oct 31, 2016, 4:41:55 PM10/31/16
to Pietro Gagliardi, Peter Mogensen, golang-nuts
* Pietro Gagliardi:

> Just pass the null-terminated string and use C.int(len(goString)) as
> the length. The length of a Go string is already in bytes and does not
> include the terminating null (since Go has none), and I assume
> C.CString() produces the same byte sequence without encoding
> conversions.

Is C.int really the right type here?

Pietro Gagliardi

unread,
Oct 31, 2016, 4:50:51 PM10/31/16
to Florian Weimer, Peter Mogensen, golang-nuts
I was going by the example at the bottom of the OP. Use whatever type is expected by the C API, of course.
Reply all
Reply to author
Forward
0 new messages