What's the correct way to return a pointer to a string in cgo?

2,680 views
Skip to first unread message

Ray Yang

unread,
Apr 16, 2016, 9:37:06 AM4/16/16
to golang-nuts
Hi there,

I have a C function which looks like the following:

bool funcA(const char* returnValue);

If I want to call it from golang, how should I declare a variable and pass it to funcA as the parameter returnValue?

I'm totally lost at the moment of how to do this and google does not give me any useful clues.

Thanks in advance.

Jan Mercl

unread,
Apr 16, 2016, 9:54:53 AM4/16/16
to Ray Yang, golang-nuts

On Sat, Apr 16, 2016 at 3:37 PM Ray Yang <rayya...@gmail.com> wrote:


> I'm totally lost at the moment of how to do this and google does not give me any useful clues.

Googling for 'golang c string', first result, in section https://golang.org/cmd/cgo/#hdr-Go_references_to_C

Use this function 

// 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

--

-j

Ray Yang

unread,
Apr 17, 2016, 11:51:07 PM4/17/16
to golang-nuts, rayya...@gmail.com
Sorry that I used the wrong function signature to describe my problem.

The actual one is this:

bool funcA(const char** returnValue);

The docs from googling just say how to pass gostring to c function but my use is different so I was lost at the beginning.

Now after some experiment, I have the following solution to have returnValue passed back from the C callee. However, as it's said that C.GoString allocates some memory, I have to free it in the C function before set it to a new value.

Is there a better way to avoid such extra memory freeing? I try to use "c_retValue := C.CString(nil)" but "go build" complains "cannot use nil as type string in argument to _Cfunc_CString"

Thanks.

========================
package main

/*
#include <stdlib.h>
#include <stdbool.h>
const char* ret = "returned valueafafas";
bool funcA(const char** returnValue) {
if (*returnValue) {
free((void*)*returnValue);
*returnValue = "Not null";
} else {
*returnValue = "Null pointer";
}

return true;
}
*/
import "C"
import (
"fmt"
)

func main() {
c_retValue := C.CString("")

ret := bool(C.funcA(&c_retValue))
if ret == true {
fmt.Println(C.GoString(c_retValue))
}
}

Ian Lance Taylor

unread,
Apr 18, 2016, 12:54:58 AM4/18/16
to Ray Yang, golang-nuts
On Sun, Apr 17, 2016 at 8:51 PM, Ray Yang <rayya...@gmail.com> wrote:
>
> Now after some experiment, I have the following solution to have returnValue
> passed back from the C callee. However, as it's said that C.GoString
> allocates some memory, I have to free it in the C function before set it to
> a new value.
>
> Is there a better way to avoid such extra memory freeing? I try to use
> "c_retValue := C.CString(nil)" but "go build" complains "cannot use nil as
> type string in argument to _Cfunc_CString"

Don't pass C.CString(nil). Write something like
var cRetValue *C.char
ret := bool(C.func(&cRetValue))

(And, for style reasons, don't write `if ret == true`, just write `if ret`.)

Ian

Ray Yang

unread,
Apr 18, 2016, 1:43:07 AM4/18/16
to golang-nuts, rayya...@gmail.com
Thanks! I didn't realize that I can declare a *C.char variable directly.

Tamás Gulácsi

unread,
Apr 18, 2016, 5:26:05 AM4/18/16
to golang-nuts, rayya...@gmail.com
2016. április 18., hétfő 7:43:07 UTC+2 időpontban Ray Yang a következőt írta:
Thanks! I didn't realize that I can declare a *C.char variable directly.

You can. But as that is a pointer, it's main reason is to point somewhere - for example to a previously allocated array (see C.CString).
But here you pass a _pointer_ to this pointer, so C side will change the value of that pointer - this pointer. So this can be nil/NULL/0,
the allocation will happen on the C side.
So after all, you'll have to clear it up on the C side - either with C.free, or with some  side effect in the C API.

Ray Yang

unread,
Apr 18, 2016, 8:04:42 PM4/18/16
to golang-nuts, rayya...@gmail.com
Agree for the dynamically allocated string from C side. But in my example, I'm returning a "const char*" which is not dynamically allocated (in normal C program, it's statically embedded in the executable file). So on golang side, should I still call C.free? Is the memory still invisibly allocated in this case?

Thanks

Ian Lance Taylor

unread,
Apr 18, 2016, 8:15:48 PM4/18/16
to Ray Yang, golang-nuts
On Mon, Apr 18, 2016 at 5:04 PM, Ray Yang <rayya...@gmail.com> wrote:
> Agree for the dynamically allocated string from C side. But in my example,
> I'm returning a "const char*" which is not dynamically allocated (in normal
> C program, it's statically embedded in the executable file). So on golang
> side, should I still call C.free? Is the memory still invisibly allocated in
> this case?

The cgo tool will not introduce any invisible memory allocation. If
you call malloc yourself then you should call free when done. If you
do not call malloc, you do not need to call free. Note that functions
like C.CString will call malloc for you, as documented.

Ian

> On Monday, April 18, 2016 at 2:26:05 AM UTC-7, Tamás Gulácsi wrote:
>>
>> 2016. április 18., hétfő 7:43:07 UTC+2 időpontban Ray Yang a következőt
>> írta:
>>>
>>> Thanks! I didn't realize that I can declare a *C.char variable directly.
>>>
>> You can. But as that is a pointer, it's main reason is to point somewhere
>> - for example to a previously allocated array (see C.CString).
>> But here you pass a _pointer_ to this pointer, so C side will change the
>> value of that pointer - this pointer. So this can be nil/NULL/0,
>> the allocation will happen on the C side.
>> So after all, you'll have to clear it up on the C side - either with
>> C.free, or with some side effect in the C API.
>
> --
> 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.

Adyuor

unread,
Jul 26, 2016, 8:13:43 PM7/26/16
to golang-nuts, rayya...@gmail.com
How one would increment cRetValue pointer in go code to access the successive bytes ?

*cRetValue gives me the zeroth byte only. How to get the 2nd, 3rd byte? The case I have has a pointer to array of structs in C land and I want to iterate over them in Go.

Thanks!!


On Sunday, April 17, 2016 at 9:54:58 PM UTC-7, Ian Lance Taylor wrote:

Tamás Gulácsi

unread,
Jul 27, 2016, 1:10:20 AM7/27/16
to golang-nuts
retValue:=C.GoString(cRetValue)
Reply all
Reply to author
Forward
0 new messages