How to handle [*c]const [*c]const u8 in a C library?

Hi All,

Hope you are well.

I translate-c a library from C. I found that it translates array of a string const char* const* to [*c]const [*c]const u8.

So in C, we can do:

static const char* const my_string_array[] = {
    "aaa",
    "bbb",
    "ccc"
};

But in Zig, if we do:

const my_string_array = [_][]const u8{
    "aaa",
    "bbb",
    "ccc",
};

We will get this error: expected type '[*c]const [*c]const u8', found '[3][]const u8' when we use this string array in functions of that C library. I tried &my_string_array and @constCast(&my_string_array) , both don’t work.

If I do:

const my_string_array = [_][*]const u8{
    "aaa",
    "bbb",
    "ccc",
};

Then I pass it as aNiceFunctionInC(&my_string_array). This can pass the compile, but it will have seg fault when running.

So the question is how can we properly use a Zig array of strings like [_][]const u8 in those C libraries which is [*c]const [*c]const u8?

Thank you in advance!

Sincerely,
IceGuye

If aNiceFunctionInC(&my_string_array) does not take the length of my_string_array as an additional argument, presumably it expects the array of pointers to be null terminated.

const my_string_array = [_:null]?[*:0]const u8{
    "aaa",
    "bbb",
    "ccc",
};

Thank you for your answer. Tried this way. Similar to

const my_string_array = [_][*]const u8{
    "aaa",
    "bbb",
    "ccc",
};

It passes the compilation with no error, but the aNiceFunctionInC(&my_string_array) still get seg fault at the same address.

There is an important distinction between [_:null]?[*:0]const u8 and [_][*]const u8: the former will have a null sentinel element after the "ccc" element.

You haven’t told us which C function you are calling or how it is implemented, but since you mentioned segfaults, it sounds like it expects a null-terminated array of values. Your code doesn’t terminate your array with a null sentinel, so it might be that the C function is reading beyond the bounds of the buffer until it segfaults.

If you’re unfamiliar with sentinels in Zig, I suggest you read up on sentinel-terminated arrays, pointers and slices in the docs.

2 Likes

Thank you for your answer and suggestion. I think it’s more about the C function thing. I don’t think I fully understand the C function for now. I guess I will spend my own time to take a look into it and its documentation. Thanks again.

Thank you everyone. It turns out that was an unrelated issue that caused the segfault.

The C function actually take another argument to determine the length of the string array, which somehow hidden in another struct… So the original [_][*]const u8 works. The [_:null]?[*:0]const u8 works too. I guess it’s because the sentinel-terminated arrays has the same len anyways.

Thank you again everyone.

1 Like

You should probably keep the :0 as I doubt it’s being passed an array with the length of each string, it probably only works atm because you are using string literals which are always null terminated, if you ever pass it non terminated strings you will have problems

2 Likes

I think the function is doing some sorts of comparison. For example, the library has a dictionary somewhere, {“aaa”, “bbb”, “ccc”, “ddd”, “eee”}, then it just checks if the strings I passed to it are one of them, so they already know the length of each possible string it should be.

But I think for memory safety reason, you are probably right. Function only knows the right strings, not other wrong ones I passed to it. It depends on how they implement the comparison, and how they handle the wrong strings with different length.

I guess I should use the sentinel-terminated arrays at least for the strings themselves.

Thanks for your advisory!

Even if the right string is passed, if it’s not null terminated, it’s still memory unsafe.
Chances are it hashes the strings, meaning it will always read up to the null character, meaning it won’t work without it.

not sure why you think it might be memory safe if it sometimes works, not how memory safety works

1 Like