
Если посмотреть, какой код генерируется для экспортируемых для доступа из С функций GO c параметром типа string,
//export MyGoCallback
func MyGoCallback(param1 string) {
…
}
то можно увидеть, что тип param1 в сгенерированной функции — это GoString. Это структура, которая объявляется в генерируемом при построении включаемом файле _cgo_export.h как
typedef struct { const char *p; GoInt n; } GoString;
Если string — это структура GoString, почему бы просто не передавать ее адрес в C, а не выделять память, копировать строку в выделенный буфер, освобождать память после использования?
Прилагаемый пример реализует схему с передачей строки без выделения памяти. При этом в функцию на C передается адрес объекта string как unsafe.Pointer и далее, после приведения типа к указателю на GoString, получаем прямой доступ к данным строки. При этом нужно учесть, что строка в кодировке utf8 и не завершена нулем.
Реализация
Код модуля на GO (gostring.go):
package main
// extern void Test(void* p);
import "C"
import "unsafe"
func main() {  
  str := "test test"
  C.Test(unsafe.Pointer(&str))
}
Код функции Test() на C (test.c):
#include <_cgo_export.h>
#include <string.h>
#include <stdio.h>
void Test(void* p)
{
  GoString* gs = (GoString*)p;
  char buf[64];
  int n = gs->n < sizeof(buf) - 1 ? gs->n : sizeof(buf) - 1;
  strncpy(buf, gs->p, n);
  buf[n] = '\0';
  printf("received: %s\n", buf);
}
В результате построения примера командой
go build .
и запуска приложения видим на экране:
received: test test
Функция Test обрезает строку, если ее длина превышает 63 символа. В реальной ситуации нужно использовать буфер большего размера и использовать динамически выделенный буфер, если этого размера оказывается недостаточно. В прилагаемых файлах gostr2c.c и gostr2c.h можно найти полную реализацию функций для использования пришедшего из кода на GO указателя на объект string. С использованием макросов из gostr2c.h функцию Test можно переписать как:
void Test(void* p)
{
  GO2C(p);
  printf("received: %s\n", pp);
  GO2C_FREE(p);
}
Заключение
Почему же этот способ не документирован в GO? Всегда ли предложенная схема будет работать? Читайте об этом во второй части…
