#Converting C code to Zig (pointer confusion)

1 messages Β· Page 1 of 1 (latest)

velvet imp
#

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!

river lily
#

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 });
velvet imp
#

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

river lily
#

Have a safe flight ✈️

velvet imp
#

thanks πŸ™‚

peak plank
#

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.

winter monolith
# velvet imp

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.

velvet imp
#

@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

river lily
# velvet imp <@263400066700541954> <@201094465815969794> <@456226577798135808> thanks for the...

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:

  1. most of the time you don't need a specifier, just {}
  2. 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}
  3. {d} also works on floats, which is how you get more legible output instead of scientific notation
peak plank
#

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.

velvet imp
#

sweet, thanks. that is very helpful to know about