У меня есть проект zig, интегрируемый с библиотекой C (xlib). В библиотеке есть метод, который требует, чтобы я передал ему весь массив argv в качестве параметра. В определении zig extern ожидаемый тип параметра argv библиотечной функции — [*c][*c]u8
.
Поскольку я использую Linux, я использую некроссплатформенный способ получения аргументов cli в zig.
var argv = std.os.argv;
Зиг-аргументы имеют тип [][*:0]u8
. У меня есть 2 вопроса.
[][*:0]u8
-> [*c][*c]u8
)[*c][*c]u8
?Давно я не работал на таком низком уровне. Правильно ли я понимаю, что [*c][*c]u8
— это список указателей c, где каждый указатель c является указателем на первый символ строки, завершающейся нулем?
Каков наиболее прагматичный способ приведения/сборки зиг-варгов в C-варги ( [][*:0]u8 -> [*c][*c]u8)
@ptrCast
свойство .ptr
на срезе:
const argv = std.os.argv;
const c_ptr: [*c][*c]u8 = @ptrCast(argv.ptr);
(Я не думаю, что здесь необходимо приведение указателя - он должен иметь возможность неявно приводить argv.ptr
, но отказывается неявно приводить дочернего элемента указателя)
Что именно означает [*c][*c]u8? / Правильно ли я понимаю, что [*c][*c]u8 — это список указателей c, где каждый указатель c является указателем на первый символ строки, завершающейся нулем?
Указатели зигзага немного сбивают с толку. Существуют указатели, срезы и массивы значений с различными параметрами и схожим синтаксисом:
*u8
: указатель на значение u8. Можно разыменовать (value.*
), но нельзя индексировать (value[0]
).[*]u8
: многоэлементный указатель неизвестной длины на значение u8. Можно индексировать, но нельзя разыменовывать.[*c]u8
: тип указателя элемента «один или несколько», который существует только для совместимости с C. Он может быть как разыменованным, так и индексированным.
int myvalue = 0;
int* mypointer = &myvalue;
*mypointer = 1; // allowed
mypointer[0] = 2; // allowed
[*c]
, которые допускают оба использования.[]u8
: срез, который представляет собой указатель и длину. это похоже на struct {ptr: [*]u8, len: usize}
. Его можно индексировать, но нельзя разыменовывать.[N]u8
: тип массива значений. Это не указатель, хотя и выглядит так.Указатели и фрагменты неизвестной длины могут иметь контрольное значение. [:0]u8
указывает, что срез имеет 0
после последнего элемента (slice[slice.len] == 0
). [*:0]u8
указывает, что, хотя длина неизвестна, элемент после последнего элемента массива должен быть 0
.
Как обычные указатели, так и указатели неизвестной длины могут неявно приводиться к указателям совместимости c.
Теперь, к вопросу
[*c]
один или несколько указателей элемента, содержащих
[*c]
один или несколько указателей элемента, содержащих
Вы правы в своей интерпретации.
Поскольку мы знаем, что это массивы, лучшим представлением в zig было бы [*][*:0]u8
, массив массивов. Однако translate-c
не знает этого, поэтому выдает указатели совместимости c.
[*]
указатель массива неизвестной длины, содержащий
[*:0]
неизвестная длина 0
-дозорный указатель массива, содержащий
Поскольку мы также знаем длину внешнего массива (с помощью argv), мы можем объединить указатель [*][*:0]u8
и длину usize
в срез: [][*:0]u8
.
Вот почему std.os.argv
возвращается [][*:0]u8
.
Ах, значит, моя первая попытка была правильной! Я сделал именно так, как вы предлагаете. К сожалению, я получаю ошибку из библиотеки (целочисленный параметр выходит за пределы рабочего диапазона). Я предполагаю, что это что-то специфическое для xlib, а не преобразование зигзагообразных переменных в переменные в стиле c.
Вы можете принудить без @ptrCast
, если сделаете дочерние указатели const: const c_ptr: [*c]const [*c]u8 = argv.ptr;
. В случае OP заголовок библиотеки должен будет объявить параметр как char *const argv[]
или char *const *argv
.
Я ничего не знаю о zig, но массив указателей на char, передаваемый функции xlib C, должен быть завершен нулевым указателем, чтобы отметить конец списка. В C
int main(int argc, char **argv)
массив, на который указываетargv
, содержитargc+1
указателиargv[0]
наargv[argc]
, иargv[argc]
будет нулевым указателем. Предыдущие указатели будут указателями на строки, завершающиеся нулем.argv[0]
(если оно не равно нулю, как и должно быть), традиционно интерпретируется как указание на имя программы.