mystr := "test string" C.Test(unsafe.Pointer(&mystr)) // работает
mystr1 := "test " + "string" C.Test(unsafe.Pointer(&mystr1)) // работает stradd := "string" mystr2 := "test " + stradd // Op3 C.Test(unsafe.Pointer(&mystr2)) // не работает
При выполнении программы на экран выводится:
panic: runtime error: cgo argument has Go pointer to Go pointer goroutine 1 [running]: received: test string received: test string panic(0x464780, 0xc04204a080) C:/Go/src/runtime/panic.go:500 +0x1af main._cgoCheckPointer0(0xc04204a050, 0xc04204a070, 0x1, 0x1, 0xc04204a06b) _/c_/examples/gostring/gostring/_obj/_cgo_gotypes.go:35 +0x60 main.main() c:/examples/gostring/gostring/gostring.go:20 +0x2ae
Не понятно, где cgoCheckPointer0 обнаруживает указатель на указатель. Возможно, это будет исправлено в следующих версиях. Возможно и другое — что метод, предложенный ниже, о том, как «обмануть» cgoCheckPointer0, перестанет работать.
Решение
Итак, для того, чтобы не допустить исключения, пытаемся избежать проверки как таковой, преобразовав указатель в целое число:
package main /* typedef unsigned long long ULongLong; extern void Test1(ULongLong l); */ import "C" import "unsafe" func main() { v1 := "test " v2 := "test 2" str := v1 + v2 t := uintptr(unsafe.Pointer(&str)) C.Test1(C.ULongLong(t)) }
Код функции на C в данном случае:
#include <_cgo_export.h> #include <string.h> #include <stdio.h> typedef unsigned long long ULongLong; void Test1(ULongLong v) { GoString* gs = (GoString*)v; 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); }
Заключение
При запуске программа работает. Это свидетельствует о том, что структура данных, в которой хранится строка str, ничем не отличается от того, что используется для хранения статических строковых констант и их конкатенации — это все тот же GoString, о котором мы говорили в части 1 («Передача строки без выделения памяти из функции на Go в код на C (часть 1)»).
received: test test 2