I have a zig project integrating with a c library (xlib). The library has a method that requires I pass the entire argv array to it as a parameter. In the zig extern definition the expected type of the library function's argv parameter is [*c][*c]u8.
As I am running linux I am using the non-cross-platform way of getting cli arguments in zig
var argv = std.os.argv;
The zig args are of type [][*:0]u8. I have 2 questions.
[][*:0]u8 -> [*c][*c]u8)[*c][*c]u8 mean?Its been a long time since I've worked at this low a level. Am I right in understanding that [*c][*c]u8 is a list of c pointers, where each c pointer is the pointer to the first character of a null-terminated string?
What is the most pragmatic way to cast/build the zig vargs into C vargs ( [][*:0]u8 -> [*c][*c]u8)
@ptrCast the .ptr property on the slice:
const argv = std.os.argv;
const c_ptr: [*c][*c]u8 = @ptrCast(argv.ptr);
(I don't think a pointer cast should be necessary here - it should be able to implicitly cast argv.ptr, but it refuses to implicitly cast the pointer child)
What exactly does [*c][*c]u8 mean? / Am I right in understanding that [*c][*c]u8 is a list of c pointers, where each c pointer is the pointer to the first character of a null-terminated string?
Zig pointers are a little bit confusing. There are pointers, slices, and value arrays with different options and similar syntax:
*u8 : a pointer to a u8 value. Can be dereferenced (value.*) but not indexed (value[0])[*]u8 : an unknown length many-item pointer to a u8 value. Can be indexed but not dereferenced.[*c]u8 : a one-or-many item pointer type that exists for c compatibility only. It can be both dereferenced and indexed.
int myvalue = 0;
int* mypointer = &myvalue;
*mypointer = 1; // allowed
mypointer[0] = 2; // allowed
[*c] pointers, which allow both uses.[]u8 : a slice, which is a pointer and a length. it is similar to struct {ptr: [*]u8, len: usize}. It can be indexed but not dereferenced.[N]u8 : a value array type. This is not a pointer, even though it looks like one.Unknown-length pointers and slices can have a sentinel value. [:0]u8 specifies that the slice has a 0 after the last item (slice[slice.len] == 0). [*:0]u8 specifies that while the length is not known, the item after the last item of the array should be a 0.
Both regular pointers and unknown-length pointers can cast implicitly to c compatibility pointers.
Now, to the question
[*c] one or many item pointer containing
[*c] one or many item pointer containing
You are correct in your interpretation.
Because we know these are meant to be arrays, a better representation in zig would be [*][*:0]u8, an array of arrays. translate-c doesn't know this though, so it emits c compatibility pointers.
[*] unknown-length array pointer containing
[*:0] unknown-length 0-sentineled array pointer containing
Because we also know the length of the outer array (with argv), we can combine the pointer [*][*:0]u8 and length usize into a slice: [][*:0]u8.
This is why std.os.argv returns [][*:0]u8.