Alignment and size of C++ primitive types
Asked Answered
C

1

8

In C++, it seems that for all integer types (int, long long int, std::uint16_t, ...) and for the floating point types, it is always sizeof(T) == alignof(T).

Is this compiler/platform-specific, or guaranteed to be true? Could there be a platform where int32_ts do not need to be aligned on a 32 bit boundary (as long as they don't overlap)?

Compliant answered 12/8, 2016 at 11:19 Comment(8)
Compiler / platform specific. And yes, there are platforms where a 32bit integer is not required to reside on a 32bit alignment. Actually, there are platforms possible where int32_t does not even exist.Pinhead
One exception to this that I've seen in the past is sizeof(double) == 8 but alignof(double) == 4 (I believe this is common on 32 bit platforms, probably not so much now that 64 bits is becoming the norm.)Unaccountedfor
@PaulR: The norm for what? You are aware that, for every 64bit desktop / server CPU, there are thousands of embedded 8/16 bit CPUs produced?Pinhead
@DevSolar: true, although use of double is fairly uncommon in low-end embedded applications.Unaccountedfor
@PaulR: Sorry. Allergic reaction to "all computers are desktops" sometimes leads to canned response without brain engaging. ;-)Pinhead
@DevSolar: no problem - I have similar issues (e.g. everyone seems to assume that x86 is the norm, so they just say "assembly" when they actually mean x86 assembly - grrr). Not to mention US-centric assumptions about currency formats, phone numbers, date, time, etc. Anyway, don't get me started... ;-)Unaccountedfor
@PaulR: Character sets / Unicode ignorance... I hear you. ;-)Pinhead
@Pinhead I haven't done much embedded work at all; what kind of alignment requirements do those 8/16-bit microcontrollers have for their fundamental types?Accipiter
A
13

For fundamental types, alignof(T) == sizeof(T) is true for most modern ABIs, but it is not guaranteed to be true. The C++ standard leaves alignment mostly implementation-defined, subject only to these constraints (see [basic.align] and [basic.fundamental] in C++11):

  • alignof(T) <= sizeof(T). This is not explicit in the standard, but there is never any padding between the elements of an array, so regardless of the alignment of the first element of T t[2], the second element cannot have alignment greater than sizeof(T), and therefore that is the maximum alignment for type T. Note that the alignment of a variable can be greater, for instance if alignas is used on it.

  • alignof(T) <= alignof(std::max_align_t), except possibly in the presence of alignas. Whether alignas can cause "over-alignment" (to a greater granularity than max_align_t) is implementation-defined.

  • char, signed char, and unsigned char all have the same alignment requirement (namely 1, because sizeof(char) == 1 by definition).

  • All unsigned integer types have the same alignment requirement as the corresponding signed type.

  • wchar_t, char16_t, and char32_t have the same alignment requirements as their "underlying" integer types.

There have been historical ABIs where fundamental types were not aligned to their size. One well-known example is double and long double in the original System V ABI for the 80386, which were only aligned to 4-byte granularity despite being 8 and 12 bytes wide respectively. This was because the stack pointer was only guaranteed to be aligned to 4-byte granularity, and it's a pain to align things within an activation record to a greater granularity than the stack pointer.1

Nowadays, this same issue of stack alignment may come up with vector types; for instance, on x86-64 the stack pointer is guaranteed to be aligned to a 16-byte boundary, but the hardware now supports up to 512-bit (64-byte) vectors.

Those are the only counterexamples I personally know about. It would not surprise me, however, to learn that an ABI for a memory-constrained embedded environment specified no alignment for anything (that is, alignof(T) == 1 for all T). It's not nearly as hard for the CPU to handle that as it used to be, especially if there's no memory virtualization involved.


1 Fun fact: the C++ standard does not require the implementation to have a function-call stack. It requires support for recursion, and there's something called "stack unwinding" that happens when exceptions are thrown, but everything is carefully written so that you could implement it using something other than the linear stacks we're all familiar with.

Accipiter answered 12/8, 2016 at 11:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.