Передача указателей в C

При работе с cgo возникает необходимость передавать указатели на данные из Go в C-функции. Основное правило: Go может передавать указатель на память, выделенную в Go, в C, но при этом Go-рантайм не должен перемещать эту память. Поэтому важно использовать правильные подходы для работы с указателями. В первый параметр передается указатель на данные, во второй - размер данных.

Синтаксис

goC.function(&var, C.int(size))

Пример

Рассмотрим базовый пример передачи указателя на Go-переменную в C-функцию:

gopackage main

/*
#include <stdio.h>

void print_int(int *p) {
  printf("Value: %d\n", *p);
}
*/
import "C"

func main() {
  value := 42
  C.print_int(&value)
}

Результат выполнения кода:

go"Value: 42"

Пример

Передача указателя на слайс через функцию C.CBytes:

gopackage main

/*
#include <string.h>
#include <stdlib.h>

void process_data(void *data, int size) {
  unsigned char *bytes = (unsigned char*)data;
  for (int i = 0; i < size; i++) {
    bytes[i] += 1;
  }
}
*/
import "C"
import "unsafe"

func main() {
  data := []byte{1, 2, 3, 4, 5}
  cData := C.CBytes(unsafe.Pointer(&data[0]), C.int(len(data)))
  defer C.free(cData)

  C.process_data(cData, C.int(len(data)))
}

Пример

Передача указателя на структуру в C:

gopackage main

/*
#include <stdlib.h>

typedef struct {
  int x;
  int y;
} Point;

void move_point(Point *p, int dx, int dy) {
  p->x += dx;
  p->y += dy;
}
*/
import "C"

func main() {
  point := C.Point{x: 10, y: 20}
  C.move_point(&point, 5, 10)
}

Важные ограничения

При передаче указателей из Go в C необходимо помнить о следующих правилах:

gopackage main

/*
#include <stdlib.h>

void modify_string(char *s) {
  s[0] = 'H';
}
*/
import "C"
import "unsafe"

func main() {
  // Правильно: используем C.CString для выделения памяти в C
  str := C.CString("hello")
  defer C.free(unsafe.Pointer(str))

  C.modify_string(str)

  // Неправильно: передача указателя на Go-строку
  // goStr := "world"
  // C.modify_string(&goStr) // ОШИБКА!
}

Смотрите также

  • функцию C.CString,
    которая создает C-строку из Go-строки
  • функцию C.free,
    которая освобождает память, выделенную в C
  • функцию C.GoBytes,
    которая преобразует C-данные в Go-слайс
  • функцию C.CBytes,
    которая копирует Go-данные в C-память