Как преобразовать нуль-терминированный байтовый массив (C-строку) в строку в Go?
Безопасное преобразование C-строки в строку Go
В Go для работы с нуль-терминированными строками из C существует специальная функция C.GoString() из пакета cgo. Это самый правильный и безопасный способ. Однако важно понимать нюансы её использования.
Основное решение: C.GoString()
Функция C.GoString() принимает *C.char и возвращает строку Go. Она автоматически:
- Считывает байты до первого нулевого символа (
\0). - Копирует данные в управляемую память Go (строки Go иммутабельны и управляются сборщиком мусора).
- Корректно обрабатывает ASCII и UTF-8 строки.
Пример правильного использования:
package main
/*
#include <stdlib.h>
#include <string.h>
char* get_name() {
char* s = (char*)malloc(10);
strcpy(s, "Hello\0Go");
return s;
}
*/
import "C"
import "unsafe"
func getName() string {
cName := C.get_name()
defer C.free(unsafe.Pointer(cName)) // Обязательно освобождаем память!
return C.GoString(cName)
}
func main() {
name := getName()
println(name) // Выведет: Hello (останавливается на \0)
}
Важные моменты и подводные камни
- Освобождение памяти: Всегда вызывайте
C.free()для строк, выделенных черезmallocв C. Иначе будет утечка. - Нулевые байты внутри:
C.GoString()остановится на первом\0. Если вам нужны все байты до конца массива (включая нулевые), используйтеC.GoBytes()с указанием длины. - Длинные строки:
C.GoString()не имеет ограничений по длине, но копирование большой строки может быть затратным. - Пустая C-строка: Если
*C.charуказывает на\0,C.GoString()вернёт пустую строку"".
Альтернативы для специфических случаев
Если вы работаете с байтовым массивом (например, []byte), а не с *C.char, и знаете точную длину, можно использовать:
// Для массива фиксированной длины
var buf [100]byte
// ... заполнение buf данными ...
s := string(buf[:]) // Создаёт строку из всего массива (включая нули)
// Или обрезать по первому нулю:
s := string(buf[:bytes.IndexByte(buf[:], 0)])
Но в контексте cgo с нуль-терминированными строками всегда используйте C.GoString() — это идиоматичный и безопасный способ.
Ручное преобразование через unsafe.Pointer
Иногда C.GoString() недоступен (например, при работе с сырыми указателями без cgo). В таких случаях можно преобразовать нуль-терминированный массив в строку вручную, но с осторожностью:
import "unsafe"
func cStringToString(cStr *byte) string {
if cStr == nil {
return ""
}
// Находим длину строки до первого нулевого байта
length := 0
ptr := unsafe.Pointer(cStr)
for *(*byte)(unsafe.Add(ptr, length)) != 0 {
length++
}
// Создаём слайс байтов и преобразуем в строку
return string(unsafe.Slice(cStr, length))
}
Важно: Этот метод работает только если память, на которую указывает cStr, остаётся доступной и не изменяется во время работы функции. Он не подходит для cgo (там есть C.GoString), но может быть полезен при работе с низкоуровневыми API ОС или эмуляции C-строк.
Для cgo всегда предпочитайте C.GoString() — он безопаснее, проще и учитывает особенности управления памятью между Go и C.
Ответов пока нет.
Войдите чтобы оставить ответ
Блоги
Тестирование в Rust: unit, integration, e2e — полное руководство