user180247
user180247

Reputation:

Is the size of a struct required to be an exact multiple of the alignment of that struct?

Once again, I'm questioning a longstanding belief.

Until today, I believed that the alignment of the following struct would normally be 4 and the size would normally be 5...

struct example
{
  int   m_Assume_32_Bits;
  char  m_Assume_8_Bit_Bytes;
};

Because of this assumption, I have data structure code that uses offsetof to determine the distance in bytes between two adjacent items in an array. Today, I spotted some old code that was using sizeof where it shouldn't, couldn't understand why I hadn't had bugs from it, coded up a unit test - and the test surprised me by passing.

A bit of investigation showed that the sizeof the type I used for the test (similar to the struct above) was an exact multiple of the alignment - ie 8 bytes. It had padding after the final member. Here is an example of why I never expected this...

struct example2
{
  example m_Example;
  char    m_Why_Cant_This_Be_At_Offset_6_Bytes;
};

A bit of Googling showed examples that make it clear that this padding after the final member is allowed - for example http://en.wikipedia.org/wiki/Data_structure_alignment#Data_structure_padding (the "or at the end of the structure" bit).

This is a bit embarrassing, as I recently posted this comment - Use of struct padding (my first comment to that answer).

What I can't seem to determine is whether this padding to an exact multiple of the alignment is guaranteed by the C++ standard, or whether it is just something that is permitted and that some (but maybe not all) compilers do.

So - is the size of a struct required to be an exact multiple of the alignment of that struct according to the C++ standard?

If the C standard makes different guarantees, I'm interested in that too, but the focus is on C++.

Upvotes: 23

Views: 8543

Answers (9)

Jack O'Connor
Jack O'Connor

Reputation: 10926

It is possible to produce a C or C++ typedef whose alignment is not a multiple of its size. This came up recently in this bindgen bug. Here's a minimal example, which I'll call test.c below:

#include <stdio.h>
#include <stdalign.h>

__attribute__ ((aligned(4))) typedef struct {
    char x[3];
} WeirdType;

int main() {
    printf("sizeof(WeirdType) = %ld\n", sizeof(WeirdType));
    printf("alignof(WeirdType) = %ld\n", alignof(WeirdType));
    return 0;
}

On my Arch Linux x86_64 machine, gcc -dumpversion && gcc test.c && ./a.out prints:

9.3.0
sizeof(WeirdType) = 3
alignof(WeirdType) = 4

Similarly clang -dumpversion && clang test.c && ./a.out prints:

9.0.1
sizeof(WeirdType) = 3
alignof(WeirdType) = 4

Saving the file as test.cc and using g++/clang++ gives the same result. (Update from a couple years later: I get the same results from GCC 11.1.0 and Clang 13.0.0.)

Notably however, MSVC on Windows does not seem to reproduce any behavior like this.

Upvotes: 2

Stack Overflow is garbage
Stack Overflow is garbage

Reputation: 248269

C++ doesn't explicitly says so, but it is a consequence of two other requirements:

First, all objects must be well-aligned.

3.8/1 says

The lifetime of an object of type T begins when [...] storage with the proper alignment and size for type T is obtained

and 3.9/5:

Object types have *alignnment requirements (3.9.1, 3.9.2). The alignment of a complete object type is an implementation-defined integer value representing a number of bytes; an object is allocated at an address that meets the alignment requirements of its object type.

So every object must be aligned according to its alignment requirements.

The other requirement is that objects in an array are allocated contigulously:

8.3.4/1:

An object of array type contains a contiguously allocated non-empty set of N subobjects of type T.

For the objects in an array to be contiguously allocated, there can be no padding between them. But for every object in the array to be properly aligned, each individual object must be padded so that the byte immediately after the end of the object is also well aligned. In other words, the size of the object must be a multiple of its alignment.

Upvotes: 4

peterchen
peterchen

Reputation: 41116

So to split your question up into two:

1. Is it legal?

[5.3.3.2] When applied to a class, the result [of the sizeof() operator] is the number of bytes in an object of that class including any padding required for placing objects of that type in an array.

So, no, it's not.

2. Well, why isn't it?

Here, I cna only speculate.

2.1. Pointer arithmetics get weirder
If alignment would be "between array elements" but would not affect the size, zthigns would get needlessly complicated, e.g.

(char *)(X+1) != ((char *)X) + sizeof(X)

(I have a hunch that this is required implicitely by the standard even without above statement, but I can't put it to proof)

2.2 Simplicity
If alignment affects size, alignment and size can be decided by looking at a single type. Consider this:

struct A  {  int x; char y;  }
struct B  { A left, right;   }

With the current standard, I just need to know sizeof(A) to determine size and layout of B.
With the alternate you suggest I need to know the internals of A. Similar to your example2: for a "better packing", sizeof(example) is not enough, you need to consider the internals of example.

Upvotes: 1

AProgrammer
AProgrammer

Reputation: 52334

5.3.3/2

When applied to a class, the result [of sizeof] is the number of bytes in an object of that class, including any padding required for placing objects of that type in an array.

So yes, object size is a multiple of its alignment.

Upvotes: 21

Ben Voigt
Ben Voigt

Reputation: 283893

The standard says (section [dcl.array]:

An object of array type contains a contiguously allocated non-empty set of N subobjects of type T.

Therefore there is no padding between array elements.

Padding inside structures is not required by the standard, but the standard doesn't permit any other way of aligning array elements.

Upvotes: 8

hpsMouse
hpsMouse

Reputation: 2014

Seems the C++03 standard didn't say (or I didn't find) whether the alignment padding bytes should be included in the object representation.

And the C99 standard says the "sizeof" a struct type or union type includes internal and trailing padding, but I'm not sure if all alignment padding is included in that "trailing padding".

Now back to your example. There is really no confusion. sizeof(example) == 8 means the structure does take 8 bytes to represent itself, including the tailing 3 padding bytes. If the char in the second structure has an offset of 6, it will overwrite the space used by m_Example. The layout of a certain type is implementation-defined, and should be kept stable in the whole implementation.

Still, whether p+1 equals (T*)((char*)p + sizeof(T)) is unsure. And I'm hoping to find the answer.

Upvotes: 0

Alexander Kondratskiy
Alexander Kondratskiy

Reputation: 4275

I am unsure if this is in the actual C/C++ standard, and I am inclined to say that it is up to the compiler (just to be on the safe side). However, I had a "fun" time figuring that out a few months ago, where I had to send dynamically generated C structs as byte arrays across a network as part of a protocol, to communicate with a chip. The alignment and size of all the structs had to be consistent with the structs in the code running on the chip, which was compiled with a variant of GCC for the MIPS architecture. I'll attempt to give the algorithm, and it should apply to all variants of gcc (and hopefully most other compilers).

All base types, like char, short and int align to their size, and they align to the next available position, regardless of the alignment of the parent. And to answer the original question, yes the total size is a multiple of the alignment.

// size 8
struct {
    char A; //byte 0
    char B; //byte 1
    int C; //byte 4
};

Even though the alignment of the struct is 4 bytes, the chars are still packed as close as possible.

The alignment of a struct is equal to the largest alignment of its members.

Example:

//size 4, but alignment is 2!
struct foo {
    char A; //byte 0
    char B; //byte 1
    short C; //byte 3
}

//size 6
struct bar {
    char A;         //byte 0
    struct foo B;   //byte 2
}

This also applies to unions, and in a curious way. The size of a union can be larger than any of the sizes of its members, simply due to alignment:

//size 3, alignment 1
struct foo {
    char A; //byte 0
    char B; //byte 1
    char C; //byte 2
};

//size 2, alignment 2
struct bar {
    short A; //byte 0
};

//size 4! alignment 2
union foobar {
    struct foo A;
    struct bar B;
}

Using these simple rules, you should be able to figure out the alignment/size of any horribly nested union/struct you come across. This is all from memory, so if I have missed a corner case that can't be decided from these rules please let me know!

Upvotes: 3

user541686
user541686

Reputation: 210755

One definition of alignment size:

The alignment size of a struct is the offset from one element to the next element when you have an array of that struct.

By its nature, if you have an array of a struct with two elements, then both need to have aligned members, so that means that yes, the size has to be a multiple of the alignment. (I'm not sure if any standard explicitly enforce this, but because the size and alignment of a struct don't depend on whether the struct is alone or inside an array, the same rules apply to both, so it can't really be any other way.)

Upvotes: 10

Edward Strange
Edward Strange

Reputation: 40897

The standard says very little about padding and alignment. Very little is guaranteed. About the only thing you can bet on is that the first element is at the beginning of the structure. After that...alignment and padding can be anything.

Upvotes: 0

Related Questions