Structure padding
I created a program data alignment. In this application, I wrote for my host machine, and after that, we can also reproduce this same program for our target.
#include<stdint.h> #include<stdio.h> struct DataSet { char data1; int data2; char data3; short data4; }; int main(void) { struct DataSet data; data.data1 = 0x11; data.data2 = 0XFFFFEEEE; data.data3 = 0x22; data.data4 = 0xABCD; uint8_t *ptr; ptr = (uint8_t*)&data; uint32_t totalSize = sizeof(struct DataSet); printf("Memory address Content \n"); printf("==============================\n"); for(uint32_t i = 0 ; i < totalSize ; i++) { printf("%p , %X\n",ptr,*ptr); ptr++; } printf("Total memory consumed by this struct variable = %I64u\n",sizeof(struct DataSet)); getchar(); }
Here, I have different variables of different data types char, int, and short.
I created one structure, i.e., structure DataSet. And after that, I’ll keep all these variables(data1, data2, data3, and data4) as member elements of that structure.
You cannot initialize a structure like this → char data1 = 0x11; //This is wrong.
We just created a record. Definitions should not be initialized like that.
And after I created one variable of struct DataSet, i.e., struct DataSet Data;
After that data of data1, I initialized like this → data.data1 = 0x11; data.data2 = 0XFFFFEEEE; data.data3 = 0x22; and data.data4 = 0xABCD;
After I created one pointer variable→ uint8_t *ptr; And I initialized that pointer ‘ptr’ with the address of data.
How do you get the address?
You have to use ‘&’(address operator) → &data.
The data type of ptr = &data entity is struct DataSet * remember that. Because data is a variable of type struct DataSet. ptr is uint8_t *. So, there will be an issue. That’s why you have to typecast this uint8_t *.
After that, I print the content which is pointed by this pointer (ptr).
For that, first I take the total size of the struct. So, uint32_t totalSize = sizeof(struct DataSet); Here I just store the total size of the struct.
After that, I use a for loop to print the content of the pointer ptr.
I used printf(“%p”). First print the address, and after that, print the data, \n. print the address first (ptr) and the content (*ptr). And after that, I incremented the ptr, i.e., ptr++. The code is shown above.
What exactly am I doing here?
So, I’m actually realizing the data structure. I want to show you how exactly the member elements of the data structure are stored in memory by printing them byte by byte. That’s why I have equated the address of this structure to a char type pointer. So, we have to run this loop for these many(total size times) times.
That’s why let me take one iterator uint32_t i = 0; i < totalSize; i++. After that, let me use getchar() here to hang the application.
After that, here let’s also print the total memory consumed by the struct variable. Let me use the sizeof operator.
Let’s understand the output.
Here, the total memory consumed by this struct variable is 12 bytes. But if you count it manually this should be 8. But, the actual memory consumed by the struct variable is 12, and the extra 4 bytes are consumed by the padding, that’s what you are seeing in this output.
Let’s analyze this output. The first address is nothing but the base address of the struct variable. And from there we printed 12 consecutive memory location contents. In the first memory location the first memory element is stored, that is 11. So, that is of type char.
The interesting thing here is in the next memory location, the next member element is not stored. Look at where the next member element is stored.
The second member element is stored at this address which ends with 8. Why? Because the compiler stored that variable according to its natural size boundary.
What is us natural size boundary of an int? 0, 4, 8, C, etc. That’s why compilers stored the int member element from this address(061FE08) onwards.
And what about 061FE05,061FE06, and 061FE07 these 3 memory locations? Those are wasted. So, they are written with zeros. That’s a 0 padding. So, you just lost three bytes of memory.
The next member element is of type char. Char be stored at any place because its natural size boundary is 1. So, it can be stored at any memory location.
The next member element is short. Short is stored according to its natural size boundary, and its next natural size boundary falls in 0E. That’s why compilers stored that variable here(CD AB)and there is zero padding here.
In this structure storage, you just lost 4 bytes of memory, because of aligned data storage.
So, the total memory consumed by this struct variable is not 8 bytes, it is 12 bytes.
So, now that’s about the aligned data storage and structure padding.
One question may be bothering you why compiler does like this.
Please note that when the data is stored in an aligned fashion it becomes a lot easier for the processor to do read and write transactions with the memory. Because there will be fewer instructions dealing with the memory and there will be fewer bus transactions with a memory. So, processor execution performance will be improved if the data is stored in an aligned fashion in the memory.
If the data is stored in an unaligned fashion, more instructions have to be added to your executable to deal with the memory. To fetch the value from the memory, decode it, extract it, to do all these things a lot of instructions will be generated. Unaligned data access increases your code size. So, you can experiment this on the target.
The only negative side effect of aligned data storage is due to padding you will lose some memory locations. But, if you are memory is really precious if you can’t afford to lose your memory locations, then you can go with unaligned data storage.
We can make this as unaligned data storage by using a packed structure. So, there is a GCC attribute called packed. By using that we can convert this structure into a packet structure which results in unaligned data storage.
FastBit Embedded Brain Academy Courses
Click here: https://fastbitlab.com/course1