To understand structure padding and alignment we have to go through the following examples-
Guess the size of below structures(32-bit machine)?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
#include <stdio.h> struct test1 { char x; int y; }; struct test2 { char x; short int y; }; struct test3 { char x; double y; short int z; }; struct test4 { double y; short int z; char x; }; int main(void) { printf("sizeof(struct test1 )=%d\n",sizeof(struct test1 )); printf("sizeof(struct test2 )=%d\n",sizeof(struct test2 )); printf("sizeof(struct test3 )=%d\n",sizeof(struct test3 )); printf("sizeof(struct test4 )=%d\n",sizeof(struct test4 )); return 0; } |
Expected Output:
1 2 3 4 |
sizeof(struct test1 )= sizeof(char)+sizeof(int)=1+4=5 sizeof(struct test2 )=sizeof(char)+sizeof(short int)=1+2=3 sizeof(struct test3 )=sizeof(char)+sizeof(double)+sizeof(short int)=1+8+2=11 sizeof(struct test4 )=sizeof(double)+sizeof(short int)+sizeof(char)=8+2+1=11 |
Actual Output :
1 2 3 4 |
sizeof(struct test1 )=8 sizeof(struct test2 )=4 sizeof(struct test3 )=24 sizeof(struct test4 )=16 |
Reason to differ actual output from expected is none other than Padding and padding required because of alignment.
Alignment is not a cosmetic modification or language requirement but it’s a necessary feature due to processor architecture to save the execution cycle.To get better understanding see memory alignment.
A general purpose processor has a separate peripheral unit to store data, known as Memory.memory is basically word aligned and word size depends on address bus.Address bus is use to transfer address request from CPU to Memory while Data Bus is use to read or write data from/to requested address of memory.
Example:
For 16-bit processor–> Address bus will be 16 bit–>word length = 16-bit.
For 32-bit processor–> Address bus will be 32 bit–>word length = 32-bit.
For 64-bit processor–> Address bus will be 64 bit–>word length = 64-bit.
So lets see what does address bus length make sense for us –
Address bus length 16-bit/32-bit/64-bit means
number of Machine cycle required=data length/address-bus length(in bits)
Example:
Number of machine cycle required to process 32-bit data?
number of Machine cycle required(16-bit)=32/16= 2 machine cycle
number of Machine cycle required(32-bit)=32/32= 1 machine cycle
number of Machine cycle required(64-bit)=32/64= 1 machine cycle
So I think Now you can understand the meaning of the term word alignment.
Data alignment is the process in which processor store variables’s data into memory in such a way so that maximum data transfer efficiency can be get.
Padding is the process of insertion of reserve bytes to make every datatype word aligned in structure.
Here is the table to get aligned address :
Data Type | Size(32-bit) | Storage Base Address (for Alignment) |
---|---|---|
char | 1 | Anywhere |
short int | 2 | Base address must be divisible by 2 |
int | 4 | Base address must be divisible by 4 |
long | 8 | Base address must be divisible by 4 |
float | 4 | Base address must be divisible by 4 |
double | 8 | Base address must be divisible by 8 |
Padding is the process of insertion of reserve bytes to fill the gap between the structure member cause due to alignment.
Now let’s understand above example :
A.
1 2 3 4 5 6 |
struct test1 { char x; char padded_byte[3]; // 3 byte padded to make char 4 byte aligned int y; }; |
Explanation:
Address | Member | Comment |
---|---|---|
0x00000000 | x | char data can store anywhere in Memory |
0x00000001 | padded_byte[0] | int member can not stored here as it is not divisible by 4 |
0x00000002 | padded_byte[1] | int member can not stored here as it is not divisible by 4 |
0x00000003 | padded_byte[2] | int member can not stored here as it is not divisible by 4 |
0x00000004 | y | int member can stored here as it is divisible by 4 |
Total Size | 8 byte | 1(x)+3(padded)+4(y) |
B.
1 2 3 4 5 6 |
struct test2 { char x; char padded_Byte; /*1 byte padding to make short int 2 byte aligned*/ short int y; }; |
Explanation:
Address | Member | Comment |
---|---|---|
0x00000000 | x | char data can store anywhere in Memory |
0x00000001 | padded_byte | short int member can not stored here as it is not divisible by 2 |
0x00000002 | y | short int member can stored here as it is divisible by 2 |
Total Size | 4 byte | 1(x)+1(padded)+2(y) |
C.
1 2 3 4 5 6 7 8 |
struct test3 { char x; char padded_byte_0[7]; double y; short int z; char padded_byte_1[6]; }; |
Explanation:
Address | Member | Comment |
---|---|---|
0x00000000 | x | char data can store anywhere in Memory |
0x00000001- 0x00000007 | padded_byte_0[0]-padded_byte_0[7] | double member can not stored here as it is not divisible by 8 |
0x00000008- 0x0000000F | y | double member can stored here as it is divisible by 8 |
0x00000010- 0x00000011 | z | short int member can stored here as it is divisible by 2 |
0x00000012- 0x0000001F | padded_byte_1[0]-padded_byte_1[5] | To Maintain alignment in case of array of structure |
Total Size | 24 byte | 1(x)+7(padded_0)+8(y)+2(z)+6(padded_1) |
D.
1 2 3 4 5 6 7 |
struct test4 { double y; short int z; char x; char padded_byte_0[5]; }; |
Explanation:
Address | Member | Comment |
---|---|---|
0x00000008- 0x0000000F | y | double member can stored here as it is divisible by 8 |
0x00000010- 0x00000011 | z | short int member can stored here as it is divisible by 2 |
0x00000012 | x | char store anywhere |
0x00000013- 0x0000001F | padded_byte_0[0]-padded_byte_0[4] | To Maintain alignment in case of array of structure |
Total Size | 24 byte | 8(y)+2(z)+1(x)+5(padded_1) |
.As we can see padding does the lots of memory waste whether it increases the performance.Some of the processor does much focus of memory size rather than its performance then they restrict alignment or use methods like #pragma to decide the own alignment boundary to reduce padding effect.This customize packing of data into memory is known as data packing.
Use of #pragma
Syntax:
#pragma pack (alignment_byte)
let’s see above example with pragma pack –
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
#include <stdio.h> #pragma pack (2) struct test1 { char x; int y; }; struct test2 { char x; short int y; }; struct test3 { char x; double y; short int z; }; struct test4 { double y; short int z; char x; }; int main(void) { printf("sizeof(struct test1 )=%d\n",sizeof(struct test1 )); printf("sizeof(struct test2 )=%d\n",sizeof(struct test2 )); printf("sizeof(struct test3 )=%d\n",sizeof(struct test3 )); printf("sizeof(struct test4 )=%d\n",sizeof(struct test4 )); return 0; } |
Output
1 2 3 4 |
sizeof(struct test1 )=6 sizeof(struct test2 )=4 sizeof(struct test3 )=12 sizeof(struct test4 )=12 |
So as you can see every structure member is aligned at 2-byte boundary.
You can also check with other alignment boundaries like 1 ,4,byte etc.