Раздел 41 · Zig с нуля

Взаимодействие с C

junior~15 мин

без входаВойди, чтобы отмечать прогресс, вести заметки и решать задачи в редакторе. войти

Взаимодействие с C

Ты умеешь говорить с коллегой, но его склад использует старые карточки. Чтобы обменяться заказом, нужно согласовать формат: какое поле означает число, где заканчивается строка и кто вернёт упаковку. Существующую C-библиотеку тоже нельзя подключить одним общим обещанием «типы похожи».

Возьмём собственную крошечную функцию C. Она получает адрес байтов и их количество, считает сумму и возвращает число. Так вся граница видна в трёх файлах.

Одна мысль урока

Совместимость с C требует согласовать объявления, реализацию, линковку и договор данных.

Опора из прошлого урока: Система сборки и кросс-компиляция.

От картинки к записи

ABI

задаёт соглашение, по которому две части понимают параметры и результат. Сходство названий типов недостаточно. Для простой C-функции используем типы из стандартных заголовков: uint8_t, uint32_t и size_t.

Заголовок sum.h объявляет функцию. Файл sum.c содержит её реализацию. Объявление позволяет Zig проверить вызов, но не создаёт машинный код функции. Если забыть подключить sum.c к сборке, возникнет проблема линковки: имя известно, реализации в артефактах нет.

Для собственного небольшого заголовка удобно использовать b.addTranslateC. Он переводит объявления C в Zig-модуль. createModule() получает этот модуль, а root .imports связывает имя c с переведёнными объявлениями. В исходнике Zig используется @import("c"), а не поиск произвольного файла в системе.

root.addCSourceFile добавляет реализацию к сборке. root.addIncludePath даёт C-компилятору каталог заголовка. .link_libc = true объявляет связь со стандартной C-библиотекой. Это несколько разных обязанностей: поиск объявления, компиляция реализации и линковка. Их полезно различать, когда ошибка относится не к той строке, которую ты только что прочитал.

Функция получает const uint8_t *bytes и size_t len. Это адрес и длина отдельно. В Zig у среза есть .ptr и .len, поэтому вызов передаёт оба. Договор требует, чтобы за адресом действительно существовало len доступных байтов на всё время вызова. C не узнаёт длину из указателя автоматически.

Строка с нулевым завершителем нужна не любой C-функции. Наша принимает длину и допускает ноль внутри данных. API вроде функций для C-строк остановился бы на первом нуле. Поэтому сначала читают объявленный договор, затем выбирают []const u8 или завершающее представление. Не добавляй ноль на всякий случай, если он меняет данные задачи.

Если C возвращает память, нужно узнать, кто её выделил и как освободить. allocator.free Zig не заменяет универсально C free для любого блока. В нашем примере C ничего не выделяет и не сохраняет адрес после возврата, поэтому договор простой и не требует нового владельца.

Проверяем настоящую интеграцию вызовом из Zig-теста. Отдельный тест чистого арифметического аналога не доказал бы, что заголовок, параметры и линковка согласованы. Проверка курса извлекает проект прямо из листингов и собирает C и Zig вместе. Дополнительно ниже есть короткий пример с системным заголовком, который можно проверить отдельным zig test -lc.

Полный пример

Сохрани листинг как lesson.zig. Запусти zig test lesson.zig -lc.

const std = @import("std");
const c = @cImport({
    @cInclude("stdlib.h");
});

test "call a real libc function" {
    try std.testing.expectEqual(@as(c_int, 7), c.abs(-7));
}

Пройди пример по шагам

Короткий пример вызывает настоящую функцию C-библиотеки, а не замену на Zig. Сохрани его как libc_test.zig и выполни zig test libc_test.zig -lc. c_int обозначает целый тип интерфейса C; не считай его автоматически u32 на всех целях.

Проект ниже сложнее только числом файлов. Вход { 1, 2, 255 } должен дать 258, а пустой срез ноль. Проверки охватывают данные, которые нельзя корректно обработать как C-строку: длина, а не первый ноль, задаёт конец.

премиум-урок

Продолжение в премиуме: 1 200 ₽ в месяц или 10 000 ₽ в год. Вместе с ним серверные проверки задач и школьные токены AI-коуча.

Оплата пока не подключена: премиум и курсы открываются промокодом.