#@offsetOf and arrays

1 messages · Page 1 of 1 (latest)

zealous moat
#

New to Zig and loving it so far!
If I run the below I have the offset of b reported as 0, whereas if I change a to u32 it reports 4. Is the 0 expected response?

Interestingly @sizeOf correctly reports 8 in both cases.

const std = @import("std");
const Thing = struct { a: [4]u8, b: u32 };
pub fn main() void {
    std.debug.print(
        "Size is {} and offset of b is: {})",
        .{ @sizeOf(Thing), @offsetOf(Thing, "b") },
    );
}
unborn pebble
#

structs normally have no defined layout, so it's putting b first when a is [4]u8 (for alignment, probably)

#

use extern struct if you want the fields to stay strictly ordered

zealous moat
#

Ah - interesting! didn't think to look at @offsetOf(Thing, "a") - which does indeed come up as 4

#

Many thanks for the reply!

zenith ingot
# zealous moat New to Zig and loving it so far! If I run the below I have the offset of `b` rep...

To add some more info: the compiler (currently) sorts fields by alignment, putting the highest alignment first. This means that this struct gets packed more closely than it would if it were an extern struct:

const Foo = struct {
    a: u8,
    b: u32,
    d: u8,
    c: u16,
};

Since the compiler optimizes the ordering, you end up with the order b, c, a, d which can be tightly packed with no padding, meaning @sizeOf(Foo) == 8.
With the original field order, there would be 3 bytes of padding between a and b, and another byte of padding between d and c, resulting in @sizeOf(Foo) == 12.

#

However, it's important to note that you should not rely on this ordering. This is just what the compiler does right now - it is under no obligation to keep doing it this way, and it's very likely it'll change, at least in debug mode

forest perch
#

the compiler could choose to randomize field order in debug mode simply to ensure that you don't rely on it

zenith ingot
#

(in fact, there's some talk about randomizing field order in debug mode, specifically to ensure people aren't relying on the layout :)

#

ah, ink beat me to it ^^

#

There's also a possibility the compiler might add extra hidden fields to structs in debug mode, to aid with safety checks

#

(eg. for undefined checking)

#

In fact, union already does this! There's a hidden tag in debug mode to ensure you don't accidentally access a field that's not active

#

tl;dr if you need defined layout use extern :)

unborn pebble
#

or packed, but you usually don't need that one much (it bitpacks against a backing integer type)

zenith ingot
#

packed has a different purpose

zealous moat
#

My structure is the header for a Wav file. I was experimenting with a little meta programming to serialise it (overkill, but fun and helps with the learning).

One of the fields is "the size of the data (which comes after the header) + the size of the remaining header after this field". I thought it would be clearer to use @offsetOf to show that's what the code is doing. So extern struct probably is what I want here (or just hard code the answer which is 36!)

zenith ingot
#

extern struct sounds like exactly the right solution for this case :)

unborn pebble
zenith ingot
#

it doesn't have consistent memory layout though

#

The byte layout differs based on endianness

zealous moat
#

...and I don't think packed supports arrays at all does it?

zenith ingot
#

correct