Difference between struct level align(x) and field level align(x)

I am not quite sure how to phrase it properly but just was curious about difference between the followings:

pub const struct__SETJMP_FLOAT128 = extern struct {
    Part: [2]c_ulonglong align(16) = std.mem.zeroes([2]c_ulonglong),
};
pub const struct__SETJMP_FLOAT128 align(16) = extern struct {
    Part: [2]c_ulonglong = std.mem.zeroes([2]c_ulonglong),
};

I believe that you can only set the alignment of variables/fields and functions. Your first example is setting the Part field of the struct to have 16 byte alignment, while your second example is trying to set the alignment of the struct__SETJMP_FLOAT128 type variable to 16. It doesn’t really make sense to set the alignment of a type variable though: it will only exist at compile time and thus won’t have a runtime memory address.

1 Like

align constraints how data are placed in memory. align(16) means the address is divisible by 16 or the lower 4 bits of the address are zero.

  • The first, Part align(16), concerns the address of the field.
  • The second align concerns the entire placement of the struct.

Manual section about alignment: Documentation - The Zig Programming Language

I don’t think that the second align does anything as far as I can tell.

const std = @import("std");

const Type align(16) = extern struct {
    bytes: [4]u8,
    nums: [12]u8,
};

const Field = extern struct {
    bytes: [4]u8,
    nums: [12]u8 align(16),
};

pub fn main() void {
    std.debug.print("Type: size {}, align {}\n", .{ @sizeOf(Type), @alignOf(Type) });
    std.debug.print("Field: size {}, align {}\n", .{ @sizeOf(Field), @alignOf(Field) });
}
> zig run example.zig 
Type: size 16, align 1
Field: size 32, align 16

@alignOf is not what you are expecting, see: Documentation - The Zig Programming Language

To see the alignment:

const std = @import("std");

const Type align(16) = extern struct {
    bytes: [4]u8,
    nums: [12]u8,
};

const f: Type = undefined;

pub fn main() void {
    std.debug.print("alignOf={} ptr={*} aligned={}\n", .{
        @alignOf(@TypeOf(f)),
        &f,                                                                    
        std.mem.isAligned(@intFromPtr(&f), 16),
    });
}

We may be talking past each other, but I don’t see what I am misunderstanding with @alignOf. Just because that variable happens to have a 16 byte aligned address doesn’t mean that the type has 16 byte alignment. E.g. just by switching to a local variable I get the following:

const std = @import("std");

const Type align(16) = extern struct {
    bytes: [4]u8,
};

pub fn main() void {
    var f: Type = undefined;
    std.debug.print("alignOf={} ptr={*} aligned={}\n", .{
        @alignOf(@TypeOf(f)),
        &f,
        std.mem.isAligned(@intFromPtr(&f), 16),
    });
}
> zig run example.zig 
alignOf=1 ptr=align_example.Type@7ffc890100ec aligned=false

In which target/platform and zig version are you getting it misaligned?
Please also try this:

const std = @import("std");

const Type align(16) = extern struct {
    bytes: [4]u8,
};

pub fn main() void {
    const f: Type = undefined;
    const ptr: *const Type align(16) = &f;
    std.debug.print("alignOf(f)={} alignOf(ptr)={} alignOf(ptr.*)={} ptr={x} aligned={}\n", .{
        @alignOf(@TypeOf(f)),
        @alignOf(@TypeOf(ptr)),
        @alignOf(@TypeOf(ptr.*)),
        @intFromPtr(ptr),
        std.mem.isAligned(@intFromPtr(ptr), 16),
    });
}

I am sorry, I was wrong, @permutationlock is right.

zig compiler ignores align declarations from types without any error.
Align declarations are allowed in variable and function declarations.

const std = @import("std");

const Type align(16) = struct {};

pub fn main() void {
    std.debug.print("type alignOf={} ptr alignOf={} alignment={}\n", .{
        @alignOf(Type),
        @alignOf(*Type),
        @typeInfo(*Type).Pointer.alignment,
    });

    const f: Type align(16) = std.mem.zeroes(Type);

    std.debug.print("type alignOf={} ptr alignOf={} alignment={}\n", .{
        @alignOf(@TypeOf(f)),
        @alignOf(@TypeOf(&f)),
        @typeInfo(@TypeOf(&f)).Pointer.alignment,
    });
}

This displays:

❯ zig run align_example.zig
type alignOf=1 ptr alignOf=8 alignment=1
type alignOf=1 ptr alignOf=8 alignment=16

The first line is align(16) in type, and you get alignment=1
The second line is align(16) in type placed in the declaration of variable, where you get the correct alignment=16.

1 Like

Then the official documentation is very misleading. It says:
“Each type has an alignment - a number of bytes such that, when a value of the type is loaded from or stored to memory, the memory address must be evenly divisible by this number. You can use @alignOf to find out this value for any type.”

It does not say that align qualifier cannot be applied to types.
Also, the passage after an example and two paragraphs below should be moved up next to definition of alignment.
“You can specify alignment on variables and functions. If you do this, then pointers to them get the specified alignment”.

The way documentation is written now follows the best practices for a game “scavenger hunt” :slight_smile: .

1 Like

“@alignOf”:
This function returns the number of bytes that this type should be aligned to for the current target to match the C ABI.

Both alignment and alignOf documentation are correct.

  • alignOf does not return the specified alignment but the C ABI alignment
  • align does nothing in types, if you want a specific alignment for your type you have to specify align in each variable declaration.

I never said that the documentation is wrong. I said it is misleading. It is hard to parse. You yourself got it wrong on the first try (see your earlier message). And I bet you read the documentation.

I wonder why it isn’t an error.
If it isn’t supposed to do anything, why allow it in the first place, just creates confusion, is this simply a missing check in the compiler?
Or is it useful for something, or should it be made to mean something?

2 Likes

Probably this, maybe worth an issue.

ow btw was zig’s align suppose to work the same way as the MSVC’s __declspec(align)? since so far i understood if __declspec(align(x)) is declared before a struct the MSVC compiler kind of tries to make all the field have an alignment of x which is not guaranteed? or perhaps it shouldn’t be that case, since it’s just specific to MSVC I suppose.

No, this is not correct. The MSVC struct align is about the alignment of the entire struct, i.e. for the first field. There is the option /Zp and pragma pack for the struct member alignment, you can also use declspec(align) for the struct members.

Hm, that makes sense now and in the Zig’s “struct level align” case if that’s really something Zig is going to support should it behave like this?