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.

vsoftco
  • 55,410
  • 12
  • 139
  • 252
  • 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? – JGroven Mar 01 '17 at 05:28
  • @JGroven 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. – vsoftco Mar 01 '17 at 05:29
  • 7
    @JGroven, `m_` is an array, not a pointer. – chris Mar 01 '17 at 05:30
  • You can force the alignment by saying struct alignas(16) Foo{char m_[16];}, if you really want. – JGroven Mar 01 '17 at 05:30
  • @JGroven 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](http://coliru.stacked-crooked.com/a/1e2084b98213adfd). And yes, I knew that you can force alignment, in fact that's how I bumped into this example, reading about `alignas`. – vsoftco Mar 01 '17 at 05:31
  • 1
    @JGroven, [Please read this](http://stackoverflow.com/a/4810668/962089). – chris Mar 01 '17 at 05:32
  • @chris, thank you, I wish I could give you more than just the one upvote! – JGroven Mar 01 '17 at 05:35
  • 1
    @vsoftco Look in the standard - http://eel.is/c++draft/expr.alignof#3 – mpiatek Mar 01 '17 at 05:36
  • @mpiatek 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. – vsoftco Mar 01 '17 at 05:36
  • @vsoftco added as an answer so there is no need to dig into the comments – mpiatek Mar 01 '17 at 05:39
  • 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. – Chris Becke Mar 01 '17 at 05:41
  • @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. – vsoftco Mar 01 '17 at 05:43
  • 2
    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. – Chris Becke Mar 01 '17 at 05:50

1 Answers1

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).

David
  • 453
  • 3
  • 7
  • 20
mpiatek
  • 1,313
  • 15
  • 16
  • 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*. – Nawaz Mar 01 '17 at 07:38
  • @Nawaz added better explanation. – mpiatek Mar 01 '17 at 08:59
  • *"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. – Nawaz Mar 01 '17 at 09:24
  • @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. – mpiatek Mar 01 '17 at 11:14
  • 1
    With the added links and more text, I think it is much better. (Upvoted) ... I'm happy now. Got something to read tonight. :-) – Nawaz Mar 01 '17 at 11:35
  • 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. – Nawaz Mar 01 '17 at 11:49
  • @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. – mpiatek Mar 01 '17 at 12:05