I'm writing C++. I have a struct

struct ExampleStruct {
int32_t i;
bool a;
bool b;
}

I know I'm running on a modern CPU such as x86_64 or ARM64, and I know I'm using a modern compiler such as recent clang, recent gcc, or recent msvc.

The byte offsets of "a" and "b", relative to the start of ExampleStruct, are:

4 and 5
30.2%
4 and 8
16.3%
It depends on platform/compiler
44.1%
U gotta use a #pragma
9.4%
Poll ended at .
@mcc I'm voting based on some guarantees provided by the standard. I hope I know the standard well enough by now that I'm not wrong. 😂
@mcc int is 4 bytes size, 4 bytes alignment, bools are 1 byte size, 1 byte alignment. the whole struct is 8 bytes size 4 bytes alignment. there's two bytes of padding at the end. mostly this is load bearing abi stuff so it shouldn't really change underneath you, even if it's not strictly specified by the C++ standard (it is specified by the platform ABI).
@mcc e.g. i believe sizeof(bool) can still be something stupid by the C++ law, in which case you might have a fun time. but if you find a platform where `sizeof(bool)` isn't 1 you can throw it into the fire anyway.
@dotstdy And "The ABI" in question is what? The Itanium ABI? A coincidental, silent agreement between the Itanium, ARM, and MS ABIs?
@mcc depends on the platform, idk which documents you need to look at to find the answers, but yes probably all three of those are important.

@mcc @dotstdy the x86 psABI and the ARM PCS define the size and alignment of the fields.

(Compiler implementations could chose to ignore them, but then they can’t interoperate with other binaries, so it’s fairly rare and confined to more niche types.)

@dotstdy @mcc even if it's probably safe to assume the padding won't change is there any cost to using #pragma pack
@bob @mcc yes, that will make it *less* portable, since it'll be misaligned.
@mcc On the one hand, this struct’s layout is fully defined (on a per-platform basis). On the other hand, it worries me that you have a need to rely on this. On the third hand that I have grown for rhetorical purposes, https://en.cppreference.com/w/cpp/types/offsetof.html exists and even if you can’t use it wherever you actually need to know offsets, you can put it in a static_assert.
offsetof - cppreference.com

@jrose For some context:
- This particular project has a fixed, predictable set of target platforms and compilers it needs to work with.
- This particular project has automated tests, so if a platform we are running on produces surprising alignments, we'll know as soon as we run the automated test where I check that
- Although offsetof is a great suggestion, it doesn't help me here because I am doing C# FFI stuff and it is the C# code which needs to know the offsets

@jrose However, despite the automated tests, if someone had responded to my post with "uhh yo win32 has 3 bytes padding between each bool" it would have saved me a little bit of time.

someone else pointed there's no *downside* to just tossing #pragma pack in there, so i did.

@mcc @jrose there are downsides to pragma pack! you don't want it here!
@dotstdy @jrose What are they? And do the downsides persist even if, contra the example above, there are four bools instead of two?
@mcc @jrose packing ignores alignment, so you're going to end up with misaligned structures, which will make arm sad. also it's just not necessary at all for ffi, where you're just trying to match the layout of a C structure in general. If you manually align everything for your platform, it'll be fine, or if you don't care about misalignment. but if you're manually laying things out to respect alignment, why even bother using it rather than the normal C layout, which is doing the same thing?

@mcc god dammit windows

Sorry, I should have remembered the ABI is sufficiently different.

(but I maintain: put the offsetof in a static_assert.)

@jrose Sorry; I am describing a hypothetical in which the win32 ABI is different, not saying it is different.
@mcc @jrose it used to be different when c++ wasn't standardised. Win32 BOOL was 32bit. But these days, it is a byte aligned byte.
@fabs @mcc @jrose still platform dependent. some exotic platforms don't have 8-bit char/std::byte types
@PeterSommerlad @mcc @jrose yes, fortunately windows doesn't support them 😁
@mcc @jrose

#pragma pack is fine, just make sure be aware of the alignment requirements. You can add unused char[] fields to pad it manually (if needed).
@viro_ssfs @mcc If you can explicitly pad to satisfy alignment, you don’t need pragma packed!
@mcc @jrose I generally think of pragma pack as a code smell that indicates somebody should probably be treating the data as a raw uint8_t[] and accessing bytes directly instead of as a C struct (makes endianness cleaner too)
@mcc if you can modify the C / write your own C code that imports the headers you could stick the offsetof in exported constants for C# to read?
@mcc @jrose
if you have assumptions in your C# code testing those in C++ at compile time with static_assert is the best way. however, you need to ensure that tests and assumptions keep in sync. not sure if you are already doing that. but compile time is better than test run time
@mcc Strictly speaking it’s offsetof(ExampleStruct, a) and offsetof(ExampleStruct, b) and you shouldn’t use anything else if you really need this, I guess.
@fraca7 The offsets, once known, will be entered into the FieldOffset() attributes of a StructLayout(LayoutKind.Explicit) struct in C#, so unfortunately offsetof doesn't exist here :(
@mcc Ah, so it’s not 100% C++. Then pragma or __attribute__, and a bunch of static_assert just in case, because computers were a mistake.
@mcc Apologies if someone has already suggested it but this seems like a good use-case for Compiler Explorer. You could have a look at the assembly generated for various struct layouts and compiler configs, and match those to your known requirements: https://godbolt.org/z/Ychb7jvhc
Compiler Explorer - C++ (x86-64 gcc 15.2)

struct S { int32_t a; bool b; bool c; }; void set_b(S& s) { s.b = true; } void set_c(S& s) { s.c = true; }

@mcc my response is "an implementation detail and if i ever have to actually think about it something has gone horribly wrong"
@ratsnakegames Making things go wrong is my job

@mcc eldritch horror. Without a pragma I would not put money on doing horrible things to a structure such as being certain where an item was.

If forced, well I'm a bit rusty, but I'd probably do something truly icky with pointers at known offsets. Possibly even something nasty and uniony.

Or not do that to a structure, cause you're not meant to!

@syllopsium Memory exists. Many programmers wish to pretend that memory does not exist. But memory exists

@mcc oh, absolutely. One of my many in progress projects involves directly bit twiddling an Intel .hex file, which straight away requires fiddling with pointers and knowing what endian a platform is.

It's not using structures against the bytestream though :)

@mcc C++ has bitfields so you could get the desired effect without a pragma
@mhoemmen @mcc bitfields are worse for this c++/c# interop purpose: the bit layout is implementation defined and compilers dont generally usually even have pragmas to control it.
@vfig @mcc true! one could consider avoiding bool for this use case

@mcc It probably should be 4 and 5, in most situations. However, it is not guaranteed. In situations like this I would always use a check to determine the offset of variables or use sizeof() to confirm. So I voted it is implementation specific.

I have run into some weird compiler behaviour over the years so I never assume.

@mcc I'm on team "it's 4 and 5, but I'd throw some static_asserts in there to get my attention in the event of compiler/platform surprises".
@mcc i think bool is cursed since it was a late addition to C. thus i wouldn't put it past being 4 and 8, though I lean towards 4 and 5. if I have control over both sides i would replace the bools with uint8_t, or merge the booleans into a single uint32_t flags field.
You don't have to call it C++. You can call it C.
@cy I would not necessarily assume the answer to my question is the same in C and C++, especially because the code may be compiled in MSVC.
Didn't imply it was the same. Just meant you can ask for the answer for C.
@mcc In C, sizeof(bool) == 1, but in C++ sizeof(bool) == sizeof(int). b is required to be either at least sizeof(char) or sizeof(int) beyond a. So you don’t know where b is. Compilers will add padding in the absence of pragma pack, which means b is probably 4 bytes beyond a.
On a little-endian machine, reading a as 32-bit at struct+4 will give a different result to reading a as 8-bit at struct+7, whereas it would give the same result on a big-endian machine. So you don’t know where a is either.
@criffer @mcc `sizeof(bool)` is not equivalent to `sizeof(int)` in general, nor is it expected to ever be 4 bytes unless you're on a *really* weird platform.

@dotstdy @mcc You're right, it's not standardised at all. And Windows is a really weird platform.

"bool — integer type, capable of holding one of the two values: true or false. The value of sizeof(bool) is implementation defined and might differ from 1"

https://en.cppreference.com/w/cpp/language/types.html

Fundamental types - cppreference.com

@criffer Windows is not that weird though. It's 1 there too. :') You might be confused by the old `BOOL` type in win32 headers, which is indeed 32 bits.
Ironically I don't have a copy of the ANSI C standard, but I'm fairly sure sizeof(bool) == 1, except for when that bool is in a struct, and then it's (32 bit) word aligned.

That's why gcc has the "pack" pragma, if you really need your boolean struct members to be size 1.

Another way to be super duper efficient in C structs is to use bitfields.
struct ExampleStruct {
int32_t i;
bool a:1;
bool b:1;
}
(unpacked) is the same size as

struct ExampleStruct {
int32_t iii;
bool a:1;
bool b:1;
bool c:1;
bool d:1;
bool e:1;
bool f:1;
bool g:1;
bool h:1;
bool i:1;
bool j:1;
bool k:1;
bool l:1;
bool m:1;
bool n:1;
bool o:1;
bool p:1;
bool q:1;
bool r:1;
bool s:1;
bool t:1;
bool u:1;
bool v:1;
bool w:1;
bool x:1;
bool y:1;
bool z:1;
};
(since you can fit 32 :1 s in a 32 bit word)

CC: @[email protected]
@cy @mcc @criffer this isn't quite true, a struct has the alignment of the largest aligned member. So if you put a single bool in a struct, you'll get a struct with 1 byte size, and 1 byte alignment. pack solves a different problem, and ignores alignment completely, meaning that as the programer you need to respect platform requirements yourself, or introduce UB in your program. Also sizeof(bool) can in fact be greater than 1, it's just rare and not important for the main platforms in PC dev.
Oh, right thanks.

I think the ultimate point is, if you have to worry about the size your boolean value takes up in memory, you're probably doing something wrong.

CC: @[email protected] @[email protected]
@mcc
I haven't seen anyone respond with this, but since bool could technically be represented in one bit, could the answer be 4 and 4 in any circumstance? (as in the compiler transparently handling bit math on access of the vars in question)
@fxchip @mcc I don't believe so, since it would break unique addressability for individual fields. I *think* you need to explicitly opt-in to that with a bitfield, but I don't know the exact C++ law there. edit: In C++20 I guess if you annotated the field with `[[no_unique_address]]` the compiler would be allowed to merge them into a single byte as well.
@dotstdy
Ah, you know what, that makes sense. I kinda just figured compiled language could just be a lawless land like what I just described. I'm sure it is in other ways but at least this one seems okay 😂
@mcc

@mcc I would say "It depends" is *technically* the correct answer, but with your other constraints it's not going to depend, and I would be pretty surprised if it wasn't 4 and 5.

I HAVE encountered platforms where the answer would probably be 4 and 8 but I was so far out into the weeds at that point that I was worrying about brining enough hiking gear.

I know you love a good hike into the weeds too but it doesn't sound like you are doing that now.

@mcc (If you were curious, some DSP where the addressable byte size was 32 bits.)
@mcc Actually, thinking about it, that's wrong: On that platform, the offsets would be 1 and 2, thanks to the 32-bit bytes.
@mcc sizeof(bool) and alignof(bool) are both implementation-defined, but all modern normal-desktop-ish implementations agree that both are 1

@mcc I’m pretty sure the correct answer is offsetof(), but maybe that’s not C++?

Edit: n/m I’m apparently way late to the party. >_< ignore me.

@slembcke offsetof is correct in C++, unfortunately, although the struct is written in C++, the consumer of the struct offsets is C# >_>