What is going on is called data structure alignment, or commonly discussed in two closely related parts: data alignment and data padding.
For the processor to be able to read the bytes it needs to be set as a memory offset equal to the some multiple of the word size chunk (the word size chunk is often the amount of bytes required to store an integer), this is known as data alignment. Data padding is the process of inserting random bytes to have a proper offset with a multiple of the word size chunk. This can be done in the middle or at the end of a structure, entirely up to the compiler.
Consider the following example on a 32-bit environment. Looking at your structure:
struct emp {
int id;
char name[ 10 ];
float f;
};
If you were to create a new structure, it could be seen in memory as follows:
1. (byte for integer)
2. (byte for integer)
3. (byte for integer)
4. (byte for integer)
5. (byte for char)
6. (byte for char)
7. (byte for char)
8. (byte for char)
9. (byte for char)
10. (byte for char)
11. (byte for char)
12. (byte for char)
13. (byte for char)
14. (byte for char)
15. ***(padding byte)***
16. ***(padding byte)***
17. (byte for float)
18. (byte for float)
19. (byte for float)
20. (byte for float)
Remark:
[x] It can store an integer without any padding.
[x] It can store the 10 bytes for an array of 10 characters.
Notice that the amount of bytes for the first two fields has tallied to 14 bytes, which is not a multiple of the word size chunk 4. The compiler then inserts the proper offset of bytes.
[x] It stores two random bytes that are used to offset 14 and 4.
[x] It stores four bytes for a float.
... and hence the amount of bytes required for the emp
structure is 20 bytes (rather than the initial thought of 18). The compilers are trading performance for space efficiency.