Преобразовать байт Go [] в символ C *
У меня есть байт. Буфер, который я упаковываю с данными, используя функцию binary.Write(). Затем мне нужно отправить этот байтовый массив в функцию C. Используя Go 1.6, я не смог понять это.
buf := new(bytes.Buffer) //create my buffer
....
binary.Write(buf, binary.LittleEndian, data) //write my data to buffer here
addr := (*C.uchar)(unsafe.Pointer(&buf.Bytes()[0])) //convert buffers byte array to a C array
rc := C.the_function(addr, C.int(buf.Len())) //Fails here
Ошибка в строке, вызывающей функцию C, говорящую:
panic: runtime error: cgo argument has Go pointer to Go pointer
Функция C:
int the_function(const void *data, int nbytes);
Я смог заставить работать следующее, но преобразование байтового массива в строку показалось неправильным. Есть лучший способ сделать это? Этот метод риск побочных эффектов для данных?
addr := unsafe.Pointer(C.CString(string(buf.Bytes()[0]))
Опять же, это должно работать в Go 1.6, который ввел более строгие правила указателя cgo.
Спасибо.
2 ответа
Если вы хотите использовать свой первый подход, вам нужно создать срез вне аргументов вызова функции и избегать временно выделенного заголовка среза или внешней структуры в аргументах, поэтому cgo
проверки не видят его как указатель, хранящийся в Go.
b := buf.Bytes()
rc := C.the_function(unsafe.Pointer(&b[0]), C.int(buf.Len()))
C.CString
метод будет более безопасным, так как данные копируются в буфер C, поэтому указатель на память Go отсутствует, и нет шанса на то, что за фрагментом bytes.Buffer
будет изменен или выйдет за рамки. Вы захотите преобразовать всю строку, а не только первый байт. Этим методам нужно выделять и копировать дважды, однако, если объем данных невелик, это, вероятно, не проблема по сравнению с издержками самого вызова cgo.
str := buf.String()
p := unsafe.Pointer(C.CString(str))
defer C.free(p)
rc = C.the_function(p, C.int(len(str)))
Если 2 копии данных неприемлемы в этом решении, существует третий вариант, когда вы самостоятельно размещаете буфер C и делаете одну копию в этом буфере:
p := C.malloc(C.size_t(len(b)))
defer C.free(p)
// copy the data into the buffer, by converting it to a Go array
cBuf := (*[1 << 30]byte)(p)
copy(cBuf[:], b)
rc = C.the_function(p, C.int(buf.Len()))
Но с обоими последними вариантами не забудьте освободить указатель malloc.
Ваша программа дает сбой, потому что правила передачи указателей в C изменились в go1.6 (подробности см. На https://tip.golang.org/doc/go1.6#cgo).
Я не знаю, почему ваша программа дает сбой, поэтому я создал Go выпуск https://github.com/golang/go/issues/14546.
Но независимо от ответа по этому вопросу, я не буду использовать внутренние биты bytes.Buffer
(как вы делаете), чтобы перейти в cgo напрямую. bytes.Buffer
реализация может измениться в будущем, и ваша программа начнет загадочно ломаться. Я просто скопировал бы нужные вам данные в любую подходящую структуру и использовал бы их для передачи в cgo.