Why is alignof of a char array always 1?
Asked Answered
P

1

7

I'm reading a bit about alignment in C++, and I am not sure why the alignment of a class that contains solely a char array member is not the sizeof of the array, but turns out to be always 1. For example

#include <iostream>

struct Foo{char m_[16];}; // shouldn't this have a 16 byte alignment?!

int main()
{
    std::cout << sizeof(Foo) << " " << alignof(Foo);
}

Live on Coliru

in the code above it's clear that the sizeof(Foo) is 16, however its alignment is 1, see the output of the code.

Why is the alignof(Foo) 1 in this case? Note that if I replace char m_[16]; with a fundamental type like int m_;, then alignof(Foo) becomes what I would've expected, i.e. sizeof(int) (on my machine this is 4).

Same happens if I simply declare an array char arr[16];, then alignof(arr) will be 1.

Phytobiology answered 1/3, 2017 at 5:24 Comment(13)
Your struct, Foo, holds a character pointer m_. The rest of the memory is allocated, but m_ is simply a pointer. Does this behavior change if you decide, instead, to manually allocate 16 chars?Kliment
@Kliment Even so, the sizeof of a pointer is usually the size of a register, i.e. 32 bit or 64 bit (4 bytes or 8 bytes, respectively). And here I'd have guessed there is no decay, in C++ an array is a type by itself, shouldn't decay to a pointer here. If I simply replace char m_[16]; with char* m_; then I get the alignment of 8 bytes, as that's what the size of the pointer on my arch is.Phytobiology
@JGroven, m_ is an array, not a pointer.Margrettmarguerie
You can force the alignment by saying struct alignas(16) Foo{char m_[16];}, if you really want.Kliment
@Kliment NO NO and again NO. You can pass an array by reference in C++, and it's not going to decay to a pointer. In fact, you can even get its size, see here. And yes, I knew that you can force alignment, in fact that's how I bumped into this example, reading about alignas.Phytobiology
@JGroven, Please read this.Margrettmarguerie
@chris, thank you, I wish I could give you more than just the one upvote!Kliment
@Phytobiology Look in the standard - eel.is/c++draft/expr.alignof#3Cursorial
@Cursorial This should be an answer, thanks! Post it, it's crystal clear, and I'll gladly accept it. Probably arrays are treated differently, as they usually don't fit into a register. I'm curious though whether that's indeed why.Phytobiology
@Phytobiology added as an answer so there is no need to dig into the commentsCursorial
I have not encountered alignof before, but: just because the array has a sizeof of 16 octets does not imply the struct has an alignment requirement of 16. I would imagine that structs simply have an alignment requirement equal to their largest member (and an array the alignment requirement of its type), so would expect the struct to have an alignment of 1: the struct can be placed anywhere in memory without violating the alignment requirement of any member.Haleigh
@ChrisBecke Yes, it looks like arrays are not aligned as I thought, I mean they are, but their alignment is the alignment of the underlying type.Phytobiology
As there is no language structure that can read multiple array elements at a time - you can only read a single element at a time - the alignment requirement of an array only needs to match the alignment requirement of the arrays element type.Haleigh
C
11

Note: data alignment has been explained in details in this article. If you want to know what the term means in general and why it is an important issue read the article.

Aligment is defined in C++ as an implementation-defined integer value representing the number of bytes between successive addresses at which a given object can be allocated [6.11.1] Alignment.

Moreover alignments must be non-negative integral powers of 2 [6.11.4] Alignment.

When we calculate the alignment of a struct we have to take into account yet another rule [6.11.5] Alignment:

Alignments have an order from weaker to stronger or stricter alignments. Stricter alignments have larger alignment values. An address that satisfies an alignment requirement also satisfies any weaker valid alignment requirement.

It's not directly stated but these rules imply that struct alignment has to be at least as strict as the alignment of its most strictly aligned member. It could be bigger but it doesn't have to be and usually isn't.

So when the alignment of the struct from OP's example is decided the alignment of the struct must be no less than alignment of its only member's type char[16]. Then by the 8.3.6 [expr.alignof]:

When alignof is applied to a reference type, the result is the alignment of the referenced type. When alignof is applied to an array type, the result is the alignment of the element type.

alignof(char[16]) equals alignof(char) which will usually be 1 because of [6.11.6] Alignment:

(...) narrow character types shall have the weakest alignment requirement.

In this example:

struct Foo
{
    char c[16];
    double d;
};

double has more strict alignment than char so alignof(Foo) equals alignof(double).

Cursorial answered 1/3, 2017 at 5:38 Comment(7)
But Foo is not an array type. It is a class, which contains an array. So how does this quote apply to this case? I think it requires an explanation to fill the gap between class and array.Pollock
@Nawaz added better explanation.Cursorial
"the basic rule is that class alignment is the least common multiple of all its members alignments." ... Where did that rule come from? I think first you should explain what exactly is "alignment" and what does it imply, say to have alignment of 1, 4, 8 and so on? .... and then things will be easier to understand for people like me.Pollock
@Nawaz rephrased the answer. Trying not to make it too big, do you think it's clear now? Unfortunate phrase you cited has the same practical meaning as the current one when we take into accout alignments being powers of 2 but I agree it might have been confusing.Cursorial
With the added links and more text, I think it is much better. (Upvoted) ... I'm happy now. Got something to read tonight. :-)Pollock
I think this line "It could be bigger but it doesn't have to be and usually isn't." should rather be "It could be smaller but it doesn't have to be and usually isn't." Because if the alignment of the struct is usually taken to be the minimum of the strict alignments of the members, then it cannot be any bigger, as the strict alignment is supposed to be the upper limit.Pollock
@Nawaz "if the alignment of the struct is usually taken to be the minimum of the strict alignments of the members". It is stated in the answer that the alignment must be at least as strict as. Reffering to your statement it is usually taken as the maximum of the members but no bigger than that. Alignment cannot be smaller than the alignment of the most strictly aligned member (most strictly aligned = biggest alignment). If you for example try to apply alignas to a struct with a value smaller than most strictly aligned member the program will be ill-formed.Cursorial

© 2022 - 2024 — McMap. All rights reserved.