views:

295

answers:

5

I know that a char and an int are calculated as being 8 bytes on 32 bit architectures due to alignment, but I recently came across a situation where a structure with 3 shorts was reported as being 6 bytes by the sizeof operator. Code is as follows:

#include <iostream>
using namespace std ;

struct IntAndChar
{
    int a ;
    unsigned char b ;
};


struct ThreeShorts
{
    unsigned short a ;
    unsigned short b ;
    unsigned short c ;
};


int main()
{
    cout<<sizeof(IntAndChar)<<endl; // outputs '8'
    cout<<sizeof(ThreeShorts)<<endl; // outputs '6', I expected this to be '8'
    return 0 ;
}

Compiler : g++ (Debian 4.3.2-1.1) 4.3.2. This really puzzles me, why isn't alignment enforced for the structure containing 3 shorts?

+2  A: 

It's entirely implementation-dependent, but presumably if your system can access any of the three shorts in the struct without worrying about the alignment, it can access any short, and hence any data member, in an array of ThreeShorts without worrying about alignment. Therefore there is no need to align the structs more strictly.

For the IntAndChar example, int presumably has size 4 and the implementation is concerned with its alignment. To ensure that every int member in an array of IntAndChar is properly aligned, the struct must be padded.

The sizeof an array T[n] is exactly defined as sizeof(T) * n.

Potatoswatter
+1  A: 

This link should help: http://en.wikipedia.org/wiki/Data_structure_alignment

In ThreeShorts all members are two byte aligned.

Brian Roach
bingo!. this explains it perfectly, thanks!
Gearoid Murphy
+10  A: 

This really puzzles me, why isn't alignment enforced for t

What alignment do you want it to have ?

Shorts can be aligned on 2 byte boundaries with no ill effects(assuming common x86 compilers all over here..). So if you create an array of struct ThreeeShorts , that struct having a size of 6 is fine, as any elements in such an array will start on a 2 byte boundary.

Your struct IntAndChar contains an int, ints wants 4 byte alignment, so if you create an array of struct IntAndChar the size have to be 8 for the next element to be aligned on a 4 byte boundary.

If we didn't consider arrays, it wouldn't matter much if struct IntAndChar were 5 bytes long, the compiler would just allocate it starting on a 4 byte boundary when you create one one the stack, or use it as a compound member in another struct.

You can always get the number of elements in an array by doing sizeof(arrayofT)/sizeof(T), and array elements are guaranteed to be stored adjacently, such that the n'th element can be retreived by stepping N*sizeof(arrayelementtype) bytes from the start, and that's the main reason you'll see structs being padded at the end.

nos
great answer. +1000 if i could.
acidzombie24
It's not that I want any particular alignment, it's just that I though that 4 byte alignment was always respected on 32 bit architectures
Gearoid Murphy
If only! It would certainly make it much easier to write memory allocators... Unfortunately the alignment might differ from one type to another and there is no portable way to ask the compiler about the particular alignment of a given type, though there are tricks.
Matthieu M.
`#define alignof(type) ((char *) type x; } *)0)->x - (char *)0)`
R..
+7  A: 

That's because int is 4 bytes, and has to be aligned to a 4-bytes boundary. This means that ANY struct containing an int also has to be aligned to at least 4-bytes.

On the other hand, short is 2 bytes, and needs alignment only to a 2-bytes boundary. If a struct containing shorts does not contain anything that needs a larger alignment, the struct will also be aligned to 2-bytes.

slacker
+1  A: 

I don't know where you get the idea about char or int being calculated as "8 bytes". No, every type is calculated in accordance with its size: char as 1, int as 4 on a 32-bit platform (not 8, but 4). Alignment requirements for each type are normally the same as its size.

For this reason, when the structure contains the members of the same type, the total size of that structure will normally be the exact sum of the sizes of its members: a structure of 3 chars will have size 3, and the structure of two ints will have size 8.

Apparently type short on your platform has size 2, so, expectedly, a structure of 3 shorts has size 6, which is exactly what you observe.

However, when your structure contains members of different types, then the difference between alignment requirements of different types comes into play. If the alignment requirement of the next field is stricter than the alignment requirement of the previous field, the compiler might have to add some padding bytes between these fields (to properly align the next member), which will affect the final size of the struct. Also, the compiler might have to add some extra padding bytes after the last member of the structure to satisfy alignment requirements in an array.

For example, a structure that looks as follows

struct S {
  char c;
  int i;
};

will most likely occupy 8 bytes on your platform because of the need for 3 padding bytes after the char member. Note, char counts as 1, int as 4 and the extra 3 padding bytes between them make it 8.

Note also that this might easily introduce the dependency of the final size of the structure on the order in which the members are declared. For example, this structure

struct S1 {
  char c1;
  int i;
  char c2;
};

on your platform will probably have size 12, while this one

struct S2 {
  int i;
  char c1;
  char c2;
};

will occupy only 8 bytes. This last example is intended to illustrate that the final size of the structure cannot be expressed in terms of how many bytes each member "counts" for. The relationships between the members are also important.

AndreyT