Pass C struct from Go to C method

162 views
Skip to first unread message

Nitish Saboo

unread,
Mar 4, 2020, 6:57:21 AM3/4/20
to golang-nuts
Hi,

I have CGO project.

Following is my C header file and Go code

syslog-node.h
===============

#ifndef _TEST_H_
#define _TEST_H_

#include <stdlib.h>

typedef void (*key_value_cb)(const char* key, const char* value, size_t value_len, int work);
typedef struct Foo{
    key_value_cb cb;
    int data;
}Foo;
int initialize_engine(const char* filename, const char* module_path, Foo *cb);
int reload_pattern_db(const char* filename, Foo *cb);


cfunc.go
========
/*

#include <stdio.h>

// The gateway function
void callOnMeGo_cgo(char *key, char *value, size_t value_len, int work)
{
void ParsedData(const char *key, const char *value, size_t value_len, int work);
ParsedData(key, value, value_len, work);
}
*/
import "C"

main.go
========

var InitStruct *C.struct_Foo;

func InitializeEngine(pattern string, path string) {
pattern_db := C.CString(pattern)
module_path := C.CString(path)
if InitStruct != nil{
C.free(unsafe.Pointer(InitStruct))
}
InitStruct := (*C.Foo)(C.calloc(1, C.sizeof_struct_Foo))
InitStruct.cb = (C.key_value_cb)(C.callOnMeGo_cgo)
InitStruct.data = C.int(0)
fmt.Println(InitStruct)  <<<<<<<<<<<<<<<<<<<<<<<<<<<'&{0x4b79d0 0 [0 0 0 0]}' got printed
C.initialize_engine(pattern_db, module_path, InitStruct)

}

func ReloadPatternDB(patterndb string) {

path := C.CString(patterndb)
InitStruct = (*C.Foo)(C.calloc(1, C.sizeof_struct_Foo))   <<<<<<<Allocating a new memory
InitStruct.cb = (C.key_value_cb)(C.callOnMeGo_cgo)
InitStruct.data = C.int(0)
fmt.Println(InitStruct)    <<<<<<<<<<<<<<<<<<<<<<<<<<< '&{0x4b79d0 0 [0 0 0 0]}' got printed
C.reload_pattern_db(path, InitStruct)

}


I have a Go code where I have to call C functions 'initialize_engine' and 'reload_pattern_db' respectively(one after the other ) and pass the C struct from Go code.


The issue is after allocating memory to 'InitStruct' using calloc in 'InitializeEngine' method, the same memory location is getting printed in ReloadPatternDB method even though I am allocating a new memory to 'InitStruct' using calloc in 'ReloadPatternDB' method.

1) Is this the correct way to pass a C struct from a Go code to C code.
2) Is this a correct way to free the struct memory assigned to InitStruct using calloc on Go side ?

Thanks,
Nitish

Ian Lance Taylor

unread,
Mar 4, 2020, 12:50:03 PM3/4/20
to Nitish Saboo, golang-nuts
You haven't showed us where the same memory location is getting
printed. Note that fmt.Println is not printing the address of
InitStruct; it is printing the values of the fields. If you want to
print the address, use fmt.Printf("%p\n", InitStruct).

Also note that InitStruct is a global variable.

Ian

Nitish Saboo

unread,
Mar 5, 2020, 1:07:16 AM3/5/20
to Ian Lance Taylor, golang-nuts
Hi Ian,

Thank you for the response.Got it.
Basically, fmt.Println(InitStruct) is printing the values of fields, cb and data, for the struct Foo pointed by InitStruct.
fmt.Printf("%p\n", InitStruct) is printing the memory address.
Btw..can you please clarify the following :

1)Is this the correct way to pass a C struct from a Go code to C code.

2) Can I free the struct memory assigned to Initstruct using calloc on Go side  in the following manner ?


if InitStruct != nil{
C.free(unsafe.Pointer(InitStruct))
}

Thanks,
Nitish


Ian Lance Taylor

unread,
Mar 5, 2020, 1:50:28 PM3/5/20
to Nitish Saboo, golang-nuts
On Wed, Mar 4, 2020 at 10:06 PM Nitish Saboo <nitish....@gmail.com> wrote:
>
> Thank you for the response.Got it.
> Basically, fmt.Println(InitStruct) is printing the values of fields, cb and data, for the struct Foo pointed by InitStruct.
> fmt.Printf("%p\n", InitStruct) is printing the memory address.
> Btw..can you please clarify the following :
>
> 1)Is this the correct way to pass a C struct from a Go code to C code.

Sure, this is fine. There are other ways also, but this approach is fine.

> 2) Can I free the struct memory assigned to Initstruct using calloc on Go side in the following manner ?
>
> if InitStruct != nil{
> C.free(unsafe.Pointer(InitStruct))
> }

Yes.

Ian

Nitish Saboo

unread,
Mar 6, 2020, 7:25:46 AM3/6/20
to Ian Lance Taylor, golang-nuts
Hi Ian,

So what did I do :


main.go
========

var InitStruct *C.struct_Foo;

func InitializeEngine(pattern string, path string) {
pattern_db := C.CString(pattern)
module_path := C.CString(path)
if InitStruct != nil{
C.free(unsafe.Pointer(InitStruct))
}
InitStruct := (*C.Foo)(C.calloc(1, C.sizeof_struct_Foo))
InitStruct.cb = (C.key_value_cb)(C.callOnMeGo_cgo)
InitStruct.data = C.int(5)
fmt.Println(InitStruct)  <<<<<<<<<<<<<<<<<<<<'&{0x4b7cb0 5 [0 0 0 0]}' got printed
fmt.Printf("%p\n", InitStruct) <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<'0x1905630' got printed

C.initialize_engine(pattern_db, module_path, InitStruct)

}

func ReloadPatternDB(patterndb string) {

if InitStruct != nil{
fmt.Println(InitStruct) <<<<<<<<<<<<<<<'&{0x4b7cb0 5 [0 0 0 0]}' got printed
fmt.Printf("%p\n", InitStruct) <<<<<<<<<<<<<'0x1905630' got printed
C.free(unsafe.Pointer(InitStruct))
fmt.Printf("%p\n", InitStruct). <<<<<<<<<<<'0x1905630' got printed ...Older memory address getting printed even after freeing the memory [a]
fmt.Println(InitStruct). <<<<<<<<<<<<<<'&{0x863b470 5 [0 0 0 0]}' got printed. Why is struct fields getting printed? [b]

}
path := C.CString(patterndb)
InitStruct = (*C.Foo)(C.calloc(1, C.sizeof_struct_Foo))  
InitStruct.cb = (C.key_value_cb)(C.callOnMeGo_cgo)
InitStruct.data = C.int(5)
fmt.Println(InitStruct)    <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< '&{0x4b7cb0 5 [0 0 0 0]}' got printed
C.reload_pattern_db(path, InitStruct) <<<<<<<<<. '0x9031d40' got printed

}


InitStruct is a global variable.I am calling C functions 'initialize_engine' and 'reload_pattern_db' respectively.

1) Even after freeing the memory in 'reload_pattern_db', the older memory address is getting printed.Why? [a]

2) Do I have to explicitly point 'InitStruct=nil' after freeing the memory.

3) Why is 'fmt.Println(InitStruct)' printing the struct fields even after freeing the memory ? [b]

Please clarify what am I missing here ?

Thanks,
Nitish


Ian Lance Taylor

unread,
Mar 6, 2020, 9:25:58 AM3/6/20
to Nitish Saboo, golang-nuts
Here you are using calls to C.calloc and C.free, so you are using C
pointers and C memory allocation. That is fine. But in C, calling
C.free doesn't somehow zero out the pointer. It leaves the pointer
unchanged, and it becomes a dangling pointer that is unsafe to use.
Basically, if you use C calls, you get C behavior. That is true even
when calling the C functions from Go.

> 2) Do I have to explicitly point 'InitStruct=nil' after freeing the memory.

Yes, that will remove the dangling pointer.

> 3) Why is 'fmt.Println(InitStruct)' printing the struct fields even after freeing the memory ? [b]

Because that's how C works. C is not a memory safe language, and it
doesn't become memory safe just because you make the calls from Go.

Ian

Nitish Saboo

unread,
Mar 6, 2020, 10:19:09 AM3/6/20
to Ian Lance Taylor, golang-nuts
Hi Ian,

> 3) Why is 'fmt.Println(InitStruct)' printing the struct fields even after freeing the memory ? [b]

Because that's how C works.  C is not a memory safe language, and it
doesn't become memory safe just because you make the calls from Go.

1)You mean to say the dangling pointer will return the struct fields unless we make it 'nil' explicitly ..am i correct ?

2) Here, C.free() will completely free the memory allocation ...right ?

Thanks,
Nitish

Ian Lance Taylor

unread,
Mar 6, 2020, 10:31:36 AM3/6/20
to Nitish Saboo, golang-nuts
On Fri, Mar 6, 2020 at 7:18 AM Nitish Saboo <nitish....@gmail.com> wrote:
>
> > 3) Why is 'fmt.Println(InitStruct)' printing the struct fields even after freeing the memory ? [b]
>
> Because that's how C works. C is not a memory safe language, and it
> doesn't become memory safe just because you make the calls from Go.
>
> 1)You mean to say the dangling pointer will return the struct fields unless we make it 'nil' explicitly ..am i correct ?

What you get by dereferencing a dangling pointer is unpredictable. If
you dereference it immediately after the C.free, as you are doing, you
will typically get the same values as before the C.free. Even that
can vary depending on the behavior of the C memory allocator.

If you set the pointer to nil, then you no longer have a dangling pointer.

https://en.wikipedia.org/wiki/Dangling_pointer

> 2) Here, C.free() will completely free the memory allocation ...right ?

Yes, it will completely free the memory allocated by C.calloc.

Ian

Nitish Saboo

unread,
Mar 6, 2020, 10:55:33 AM3/6/20
to Ian Lance Taylor, golang-nuts
Thankyou Ian for your responses.

Thanks,
Nitish
Reply all
Reply to author
Forward
0 new messages