Hi, I'm new to C and Zig. I am working on a C tutorial which I am translating to Zig. I have been having trouble converting a specific part of the C code that uses pointers. The Zig compiler gives me the following error: error: element access of non-indexable type '*const u8'. I have simplified down the C code to show what I am working with. I have also simplified the Zig code to show what approach I have been trying. I will attach both files. Please let me know where I am going wrong. My primary goal is to get past this compiler error and my secondary goal is making sure I am follow good Zig patterns. I am open to comments on both π Thanks!
#Converting C code to Zig (pointer confusion)
1 messages Β· Page 1 of 1 (latest)
online c compiler that I was testing it with https://replit.com/languages/c
Welcome! I have a few pointers on how to make your code more idiomatic, but first to address your main issue:
use_pointers should take a slice, not a *const u8. The type you have in your function parameter now is a pointer-to-single-value. That type does not allow indexing because the type is telling the compiler "This points to a single value". Change it to sprite: []const u8.
This is one basic way where Zig differs from C. I recommend reading these parts of that language guide:
Another note, not critical to your issue, but to make the code more idiomatic: There's no need to use @memcpy here. If you know the default value you want for your data struct field, you can use Zig's array concatenation/multiplication operators:
const Memory = struct {
data: [4096]u8 = character_set ++ ([1]u8{0} ** 4093),
};
Finally, another way zig differs from C is there are no varargs. So the second argument to print should be a single anonymous struct (tuple) with all your arguments:
debug.print("sprite[{d}]: {d}\n", .{ ly, c });
awesome, thanks for the quick and detailed response @river lily ! I am about to get on a plane but will try these things out and let you know how I get on. is the pattern I am using to leverage an array as memory (in the same way as the c code) ok to do in zig? or should I be using an allocator for that?
I did notice in the zig docs it says that you probably shouldn't use @memcpy but I wasn't sure what other pattern to use, so I will make that refactor too
Both are ok! Static and stack memory are pretty similar to how they work in C.
Have a safe flight βοΈ
thanks π
I'll note that there's also std.mem.copy - which takes slices - which are much safer than using @memcpy yourself, unless you're very sure that what you've written is correct. (Slices have bounds checks, that @memcpy won't do.)
@memcpy is actually faster than std.mem.copy last I checked, but it's very easy to misuse, so I'd generally suggest not using it until you're very familiar with Zig, and how to avoid the gotchas with @memcpy.
I'll also note that, out of interest, it is actually possible to turn a buffer into an allocator, with the help of std.heap.FixedBufferAllocator - you don't really need it here though.
Also don't underestimate zig's for loop, sprite is iterated (like for each), c_el becomes the element, and ly_i the index of the element:
fn use_pointers(sprite: []const u8, num: u16) void {
for (sprite) |c_el, ly_i| {
debug.print("sprite[{d}]: {d}\n", .{ly_i, c_el});
// do stuff with c
}
}
You don't need .{} (tuple, or anonymous array literal) around each printed variable, you can seperate vars with comma and group them in one. .{ly, c}
If compiler complains about unused parameters or variables you can use _ = num; temporarily, until you actually use it in code.
@river lily @peak plank @winter monolith thanks for the tips. I got it working with your suggestions! is there some good documentation around substitutions when printing? I know about {s} and {d} but what about booleans or other types? I couldn't find a detailed breakdown on ziglang.org/documentation
The doc comment here is a handy guide I open from time to time: https://github.com/ziglang/zig/blob/master/lib/std/fmt.zig#L28-L77
Some quick tips:
- most of the time you don't need a specifier, just
{} - you will need
{s}to force a u8 slice to be treated as a string instead of a list of numbers, but you can format it as the latter with{any} {d}also works on floats, which is how you get more legible output instead of scientific notation
Also of note is that some things don't have specifiers, and instead rely on the custom formatting mechanism.
For example: std.debug.print("{}\n", .{ std.fmt.fmtSliceHexLower(slice) }).
fmtSliceHexLower returns a custom struct that just has a namespaced function with a specific signature:
fn format(
self: @This(),
comptime fmt: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
) @TypeOf(writer).Error!void
[std.fmt actually has a helper type which it uses to do this (std.fmt.Formatter), where you can give it a function to format with, and it returns you a custom struct that Just Works when used with the formatting mechanisms - such as std.debug.print.]
The idea here is simply that std.debug.print will call this format function in order to fulfil the {} in the format string, and that function is what actually writes out the slice as lowercase hex.
sweet, thanks. that is very helpful to know about