-1

I am trying to build a slice of pointers manually and with C.calloc() for allocating the array portion of the slice. I am able to do this successfully though when I try and add pointers that I allocate with make() some of the values (of what the pointers point to) get changed seemingly randomly. On the other hand if I C.calloc() space for the pointers I will be adding, the value are not changed. Or, if I allocate the slices with make() and the pointers I add are allocated with make() the values are not changed.

I do notice that the memory locations of the pointers when using C.calloc() vs make() are very different but I don't see why this should cause the memory to be changed randomly. I am new to Go so please forgive me if I am overlooking some very simple.

Here is the code I use for allocating my slices manually:

type caster struct {
    ptr *byte;
    len int64;
    cap int64;
}

var temp caster;
temp.ptr=(*byte)(C.calloc(C.ulong(size),8));
temp.len=int64(size);
temp.cap=int64(size);
newTable.table=*(*[]*entry)(unsafe.Pointer(&temp));

This works if the entries I add are allocated as follows:

var temp caster;
var e []entry;
temp.ptr=(*byte)(C.calloc(C.ulong(ninserts),8));
temp.len=int64(ninserts);
temp.cap=int64(ninserts);
e=*(*[]entry)(unsafe.Pointer(&temp));

for i:=0;i<ninserts;i++ {
    e[i].val=hint64(rand.Int63());
}

for i:=0;i<ninserts;i++ {
    ht.insert(&e[i]);
}

though the memory of of the entries gets randomly changed if they are allocated as follows:

var e []entry = make([]entry, ninserts); 

for i:=0;i<ninserts;i++ {
    e[i].val=hint64(rand.Int63());
}

for i:=0;i<ninserts;i++ {
    ht.insert(&e[i]);
}

Unless I build my slices normally as follows:

newTable.table = make([]*entry,  size);
Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
Noah
  • 1,647
  • 1
  • 9
  • 18

1 Answers1

2

I am trying to build a slice of pointers manually and with C.calloc() for allocating the array portion of the slice.

This is explicitly forbidden.

To quote from the official cgo documentation:

Go is a garbage collected language, and the garbage collector needs to know the location of every pointer to Go memory. Because of this, there are restrictions on passing pointers between Go and C.

In this section the term Go pointer means a pointer to memory allocated by Go (such as by using the & operator or calling the predefined new function) and the term C pointer means a pointer to memory allocated by C (such as by a call to C.malloc). Whether a pointer is a Go pointer or a C pointer is a dynamic property determined by how the memory was allocated; it has nothing to do with the type of the pointer.

Note that values of some Go types, other than the type's zero value, always include Go pointers. This is true of string, slice, interface, channel, map, and function types. A pointer type may hold a Go pointer or a C pointer. Array and struct types may or may not include Go pointers, depending on the element types. All the discussion below about Go pointers applies not just to pointer types, but also to other types that include Go pointers.

The boldface above is mine. It means that you must not allocate any of these types via C's allocators.

It is possible to defeat this enforcement by using the unsafe package, and of course there is nothing stopping the C code from doing anything it likes. However, programs that break these rules are likely to fail in unexpected and unpredictable ways.

This bit of your own code:

newTable.table=*(*[]*entry)(unsafe.Pointer(&temp));

violates the rules, but defeats their enforcement. You have allocated C memory, and are now trying to use it as if it were Go memory, in the form of a slice.

Community
  • 1
  • 1
torek
  • 448,244
  • 59
  • 642
  • 775