2016-06-25 7 views
2

Ich versuche, mit einigen C-Code von Go zu verbinden. Mit cgo war dies relativ einfach, bis ich diesen (ziemlich häufigen) Fall traf: Ich musste einen Zeiger auf eine Struktur übergeben, die selbst einen Zeiger auf einige Daten enthält. Ich kann mir nicht vorstellen, wie ich das von Go aus machen kann, ohne die Struktur in den C-Code selbst zu übernehmen, was ich lieber nicht machen würde. Hier ist ein Ausschnitt, der das Problem veranschaulicht:Wie kann ich void * C Zeiger in Go ausfüllen?

package main 

// typedef struct { 
//  int size; 
//  void *data; 
// } info; 
// 
// void test(info *infoPtr) { 
//  // Do something here... 
// } 
import "C" 

import "unsafe" 

func main() { 
    var data uint8 = 5 
    info := &C.info{size: C.int(unsafe.Sizeof(data)), data: unsafe.Pointer(&data)} 
    C.test(info) 
} 

Während dieses feine kompiliert, zu laufen versuchen, es ergibt sich:

panic: runtime error: cgo argument has Go pointer to Go pointer 

In meinem Fall die Daten an die C-Aufruf übergeben wird nicht über den Aufruf hinaus bestehen (dh der fragliche C-Code gräbt sich in die Struktur ein, kopiert was er braucht und kehrt dann zurück).

Antwort

2

Siehe "Passing pointers" Abschnitt in cgo docs:

Go Code ein Go-Zeiger passieren kann C die Go-Speicher vorgesehen, an dem er zeigt keine Go-Zeiger enthält.

Und auch:

Diese Regeln dynamisch zur Laufzeit überprüft werden. Die Überprüfung wird durch die Einstellung cgocheck der Umgebungsvariablen GODEBUG gesteuert. Die Standardeinstellung ist GODEBUG = cgocheck = 1, die relativ billige dynamische Prüfungen implementiert. Diese Überprüfungen können vollständig mit GODEBUG = cgocheck = 0 deaktiviert werden. Die vollständige Überprüfung der Zeigerhandhabung, zu bestimmten Kosten in der Laufzeit, ist über GODEBUG = cgocheck = 2 möglich.

Wenn Sie das Snippet laufen Sie mit zur Verfügung gestellt haben:

GODEBUG=cgocheck=0 go run snippet.go 

Dann gibt es keine Panik. Allerdings ist der richtige Weg zu gehen C.malloc zu verwenden (oder einen „C-Pointer“ von woanders zu erhalten):

package main 

// #include <stdlib.h> 
// typedef struct { 
//  int size; 
//  void *data; 
// } info; 
// 
// void test(info *infoPtr) { 
//  // Do something here... 
// } 
import "C" 

import "unsafe" 

func main() { 
    var data uint8 = 5 

    cdata := C.malloc(C.size_t(unsafe.Sizeof(data))) 
    *(*C.char)(cdata) = C.char(data) 
    defer C.free(cdata) 

    info := &C.info{size: C.int(unsafe.Sizeof(data)), data: cdata} 
    C.test(info) 
} 

Es funktioniert, weil während regelmäßige Go Zeiger sind nicht erlaubt, C.malloc einen „C-Zeiger“ zurückgibt:

Go Zeiger einen Zeiger auf den Speicher von Go (wie unter Verwendung des & Bediener oder Aufrufen der vordefinierten neuen Funktion) und der Begriff C Zeigermittel einen Zeiger auf Speicher zugewiesen durch C (wie beispielsweise durch einen Anruf zugeordnet bedeutet C.malloc). Ob ein Zeiger ein Go-Zeiger oder ein C-Zeiger ist, ist eine dynamische Eigenschaft, die dadurch bestimmt wird, wie der Speicher zugewiesen wurde.

Beachten Sie, dass Sie stdlib.h verwenden müssen, um C.free zu verwenden.

+1

Danke. Das ist es, womit ich endete ... Ich hoffte nur auf etwas weniger Ausführliches. –