0

In go, I'm trying to iterate through an array that was created in C. I have the length of the array (int arrc) and its pointer (mytype *arrv).

I have found a way, but it involves transferring back and forth between go and c and is super super hacky.

// void *nextelement(void *p, int i, int size) {
//      return (void*)((uint64_t)p+i*size);
// }
#import "C"

...

    for i := 0; i < protoc; i++ {
        adr := (*C.mytype)(C.nextelement(unsafe.Pointer(myarr), C.int(i), C.sizeof_mytype))


All that code just to get myarr[i]... it doesn't feel right.

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
Dellowar
  • 3,160
  • 1
  • 18
  • 37
  • update: after running with this logic and doing some testing, the code is panicking unpredictably (in otherwords, undefined behavior). So the solution I posted doesn't even work. – Dellowar Nov 16 '20 at 03:20
  • 3
    The usual trick is to take a pointer to the first element of the array and turn this into a slice: https://stackoverflow.com/q/48756732/1256452 – torek Nov 16 '20 at 03:35
  • See also he documentation here: https://github.com/golang/go/wiki/cgo#turning-c-arrays-into-go-slices – JimB Nov 16 '20 at 14:15
  • @torek / JimB, this is exactly what I was looking for. One of you should make an answer to be accepted. – Dellowar Nov 16 '20 at 19:30

1 Answers1

1

Note: this is simply copied from the CGo documentation.

Turning C arrays into Go slices

C arrays are typically either null-terminated or have a length kept elsewhere.

Go provides the following function to make a new Go byte slice from a C array:

func C.GoBytes(cArray unsafe.Pointer, length C.int) []byte

To create a Go slice backed by a C array (without copying the original data), one needs to acquire this length at runtime and use a type conversion to a pointer to a very big array and then slice it to the length that you want (also remember to set the cap if you're using Go 1.2 or later), for example (see http://play.golang.org/p/XuC0xqtAIC for a runnable example):

        var theCArray *C.YourType = C.getTheArray()
        length := C.getTheArrayLength()
        slice := (*[1 << 28]C.YourType)(unsafe.Pointer(theCArray))[:length:length]

It is important to keep in mind that the Go garbage collector will not interact with this data, and that if it is freed from the C side of things, the behavior of any Go code using the slice is nondeterministic.

The reason for the magic array size constant is described in What does (*[1 << 30]C.YourType) do exactly in CGo? The actual value of the constant, whether it's 1 << 30 or 1 << 28 or whatever, is not crucial, but it must be at least as large as the largest length value will be.

torek
  • 448,244
  • 59
  • 642
  • 775
  • `C.GoBytes` has a massive issue in that it uses `C.int` for the size type, so if you ever find yourself with more than 2 gigs of data, it will overflow and the code panics. – Xarn Aug 05 '22 at 10:36
  • @Xarn: that seems like something that should be filed as an issue with CGo... (Can now be addressed via generics, perhaps) – torek Aug 05 '22 at 15:08