7
votes

Preface: Did my research about struct alignment. Looked at this question, this one and also this one - but still did not find my answer.

My Actual Question:

Here is a code snippet I created in order to clarify my question:

#include "stdafx.h"
#include <stdio.h>

struct IntAndCharStruct
{
    int a;
    char b;
};

struct IntAndDoubleStruct
{
    int a;
    double d;
};

struct IntFloatAndDoubleStruct
{
    int a;
    float c;
    double d;
};

int main()
{
    printf("Int: %d\n", sizeof(int));
    printf("Float: %d\n", sizeof(float));
    printf("Char: %d\n", sizeof(char));
    printf("Double: %d\n", sizeof(double));
    printf("IntAndCharStruct: %d\n", sizeof(IntAndCharStruct));
    printf("IntAndDoubleStruct: %d\n", sizeof(IntAndDoubleStruct));
    printf("IntFloatAndDoubleStruct: %d\n", sizeof(IntFloatAndDoubleStruct));
    getchar();
}

And it's output is:

Int: 4
Float: 4
Char: 1
Double: 8
IntAndCharStruct: 8
IntAndDoubleStruct: 16
IntFloatAndDoubleStruct: 16

I get the alignment seen in the IntAndCharStruct and in the IntAndDoubleStruct.

But I just don't get the IntFloatAndDoubleStruct one.

Simply put: Why isn't sizeof(IntFloatAndDoubleStruct) = 24?

Thanks in advance!

p.s: I'm using Visual-Studio 2017, standard console application.

Edit: Per comments, tested IntDoubleAndFloatStruct (different order of elements) and got 24 in the sizeof() - And I will be happy if answers will note and explain this case too.

4
Switch the order of float and double and you should get 24.Gerhardh
Why would you expect to get 24? What do you expect for int and float without double and why?Gerhardh
@Gerhardh - switched the order, and did get a 24! As for my expectations, I was expecting an alignment to the biggest element - so 8 bytes * 3 elements = 24. Int and Float have the same size (4), so 8 is OK IMO.MordechayS
I like your list of links you studied, instead of just writing "I read everything." as so many other questions do. Good work.Yunnosch

4 Answers

6
votes

On your platform, the following holds: The size of int and float are both 4. The size & alignment requirement of double is 8.

We know this from the sizeof output you've shown. sizeof (T) gives the number of bytes between the addresses of two consecutive elements of type T in an array. So we know that the alignment requirements are as I've said above. (Note)

Now, the compiler reported 16 for IntFloatAndDoubleStruct. Does it work out?

Assume we have such an object at an address aligned to 16.

  • int a is therefore at address X aligned to 16, so it's aligned to 4 just fine. It will occupy bytes [X, X+4)
  • This means float c could start at X+4, which is aligned to 4, which is fine for float. It will occupy bytes [X+4, X+8)
  • Finally, double d could start at X+8, which is aligned to 8, which is fine for double. It will occupy bytes [X+8, X+16)
  • This leaves X+16 free for the next struct object, again aligned to 16.

So there's no reason to start any of the members later, so the whole struct fits into 16 bytes just fine.


(Note) This is not strictly true: for each of these, we know that both size and alignment are <= N, that N is a multiple of the alignment requirement, and that there is no N1 < N for which this would also hold. However, this is a very fine detail, and for clarity the answer simply assumes the actual size and alignment requirements for the primitive types are indetical, which is the most likely case on the OP's platform anyway.

5
votes

Your struct must be 8*N bytes long, since it has a member with 8 bytes (double). That means the struct sits in the memory at an address (A) divisible by 8 (A%8 == 0), and its end address will be (A + 8N) which will also be divisible by 8.

From there, you store 2 4-bytes variables (int + float) meaning you now occupy the memory area [A,A+8). Now you store an 8-byte variable (double). There is no need for padding since (A+8) % 8 == 0 [since A%8 == 0]. So, with no padding you get the 4+4+8 == 16.

If you change the order to int -> double -> float you'll occupy 24 bytes since the double variable original address will not be divisible by 8 and it will have to pad 4 bytes to get to a valid address (and also the struct will have padding at the end).


|--------||--------||--------||--------||--------||--------||--------||--------|
|   each ||   cell ||  here  ||represen||-ts  4  || bytes  ||        ||        |
|--------||--------||--------||--------||--------||--------||--------||--------|

A        A+4       A+8      A+12      A+16      A+20      A+24                      [addresses]
|--------||--------||--------||--------||--------||--------||--------||--------|    
|   int  ||  float || double || double ||        ||        ||        ||        |    [content - basic case]
|--------||--------||--------||--------||--------||--------||--------||--------|

first padding to ensure the double sits on address that is divisble by 8
last  padding to ensure the struct size is divisble by the largest member's size (8)
|--------||--------||--------||--------||--------||--------||--------||--------|    
|   int  || padding|| double || double || float  || padding||        ||        |    [content - change order case]
|--------||--------||--------||--------||--------||--------||--------||--------|
0
votes

Compiler will insert padding in order to guarantee that each element is at offset that is some multiple of its size.

In this case int will be at offset=0 (relative to address of a structure instance), float at offset=4, and double at offset=8, because sizes of int and float add up to 8.

There's no padding at the end - size of the structure is already 16, which is a multiple of size of double.

0
votes

First of all , C does not impose any kind of alignment for the members inside a structure. This is why it is hard to say what value sizeof() returns for an instance of some structure.

The only restriction that C imposes concerning structure members is that the address allocated for the first member to be a multiple of its alignment (such that a vector of that structure to be able to be accessed as pointer+sizeof()*index). In your case IntFloatAndDoubleStruct must be aligned at a multiple of sizeof(int), as a has the type int.

The internal members are in some cases allocated addresses multiple of their alignment in order to work faster (so some padding is inserted), but this is not imposed by C. So a compiler may decide to insert #pragma to change the algorithm used to allocate structures. See here.

As CIsForCookies suggests, the structure will be aligned with a multiple of maximum alignment, in order for a vector of such structures to still have each member aligned. But this is not imposed by C language, but by having in mind performance of implementation because in addr_struct[i]+member_offset both terms are divisible by alignment.