Use of align in Zig

I was building a simple directory watcher in zig . I have taken some reference from Google gemini . Here is the code

const std = @import("std");
const linux = std.os.linux;

pub fn main() !void {
    const fd_usize = linux.inotify_init1(0);
    const fd: i32 = @intCast(fd_usize);
    defer std.posix.close(fd);

    // var buf: [4096]u8 align(@alignOf(linux.inotify_event)) = undefined;
    var buf: [4096]u8 = undefined;

    if (fd < 0) {
        std.debug.print("inotify init", .{});
        std.process.exit(1);
    }
    _ = linux.inotify_add_watch(fd, ".", linux.IN.MODIFY | linux.IN.DELETE | linux.IN.CREATE | linux.IN.MOVED_TO);
    std.log.info("Watching current dir for changes", .{});

    while (true) {
        const length = try std.posix.read(fd, &buf);

        var i: usize = 0;
        while (i < length) {
            const event = @as(*linux.inotify_event, @ptrCast(@alignCast(&buf[i])));

            // Check if the event has a filename associated with it
            if (event.len > 0) {
                const name = event.getName() orelse "genaric";

                if (event.mask & linux.IN.CREATE != 0) {
                    std.debug.print("File Created: {s}\n", .{name});
                } else if (event.mask & linux.IN.MODIFY != 0) {
                    std.debug.print("File Modified: {s}\n", .{name});
                } else if (event.mask & linux.IN.DELETE != 0) {
                    std.debug.print("File Deleted: {s}\n", .{name});
                }
            }
            // Move to the next event in the buffer
            i = i + @sizeOf(linux.inotify_event) + event.len;
        }
    }
}

Here when declaring a buffer for the file descriptor Gemini uses the

// var buf: [4096]u8 align(@alignOf(linux.inotify_event)) = undefined;
    var buf: [4096]u8 = undefined;

upper line . The program compiles with both version . What is the use of this align ?

align syntax in zig

In the case of const/var name: T align(n) it is specifying that the variable will be stored at n alignment in memory.

You can do the same thing with fields, name: T align(n) specifies that the field needs to be at n alignment in memory, this can affect the total alignment of the type it is in, depending on the alignment of other fields.

When taking a pointer to a field/variable, the pointer will have the alignment specified, see next paragraph for what that means.

There is similar application with pointers, *alignt(n) T, specifies the address being pointed too is at alignment n, this alignment is part of the type of the pointer. This works for any kind of pointer with any child type. If you have a field/variable of a pointer, you can specify both, e.g. name: *align(x) T align(y).

In all cases, if you don’t specify an explicit alignment, it will instead implicitly use the alignment of the type of the variable/field or the child type of the pointer.

what is alignment in the first place

data is only allowed to be in a memory address that is a multiple of its alignment. An alignment of 1 allows any address, as every address is a multiple of 1.
an alignment of 0 is disallowed for most types as only 0 is a multiple of 0 which is often the null address. Only 0 sized types can have an alignment of 0, as they have no data to put in memory.

Alignment increases in log2, so 1 2 4 8 etc are valid alignments, but 3 is not.

Alignment is a result of how CPUs interact with memory, modern processors can deal with improperly aligned data with unmeasurable performance differences (in simple cases). But older or niche architectures will have performance impacts or even trigger a fault if data is improperly aligned.

Therefore, alignment is important for the compiler to generate correct and performant code for older and niche processors.

All types have an alignment, for composite types such as structs or unions, the alignment will be the largest of their fields. Their size may also increase to be a multiple of their alignment, this ensures contiguous data, such as in arrays or slices, that each element will be aligned properly.

With extern structs, or languages that don’t optimise field order like c, padding will be inserted so that the next field(s) will be aligned.

8 Likes