Generally, people who work on high-level language do not take an interest in the details of the computer and their architecture. They ignore the concept of endianness and also never think about how to convert little endian to big endian and what is the difference between little endian big endian (little endian vs big endian). But we should know little endian vs big endian because some times it creates a problem when you transmitting the data serially over the network from one computer to another computer.

So knowledge of endianness is important when you are reading and writing the data across the network from one system to another. If the sender and receiver computer have different endianness, then the receiver system would not receive the actual data transmitted by the sender. So let us see what is an endianness,

What is endianness?

Endianness refers to the bytes order in which data stored in the memory and also describes the order of byte transmission over a digital link. Basically Endianness comes in two varieties little endian and big endian and in which order data will be stored in memory it depends on the endianness.

If your machine is big-endian then the MSB byte store first (means at lower address) and if the machine is the little-endian then LSB byte store first (means at lower address). Let us see the below table that contains a list of machines. In which some machines or little endian and some are big-endian.

Some examples of the little-endian and big-endian systems.

How data stored in little endian and big endian machines?

We can easily understand how data is stored in little endian and big endian machines. Let us assume, we have a 32bit processor and need to store a value in memory. this value is 0x11223344 (here we are taking value in hexadecimal for easy understanding).

Now let us see how the storing order of bytes will depend on the endianness of the system (little endian vs big endian). So here we will go first for the Big endian machine and then little endian machine.

Big-endian

As we know that in big-endian MSB Byte will store first. It means the MSB Byte will store at the lowest memory address. See the table,

Little-endian

In the little endian machine, LSB byte will store first. So the LSB Byte will store at the lowest memory address. See the table,

Note: Some processor has the ability to switch one endianness to other endianness using the software that means it can perform like both big endian or little endian at a time. This processor is known as the Bi-endian. Here are some architecture (ARM version 3 and above, Alpha, SPARC) who provide the switchable endianness (support bi-endianness) feature.

C program to check processor endianness

As we know that in little endian machine least significant byte of any multibyte data field is stored at the lowest memory address. So in the below program, we are checking the value of the lowest address. If the value is 1 then it will little endian either it will big endian.

#include <stdio.h> #include <inttypes.h> int main(void) { uint32_t data; uint8_t *cptr; data = 1; //Assign data cptr = (uint8_t *)&data; //Type cast if (*cptr == 1) { printf("little-endiann"); } else if (*cptr == 0) { printf("big-endiann"); } return 0; }

Output:

Code Explanation:

If your machine is little endian, the data in the memory will be something like the below expression:

higher memory -----> +----+----+----+----+ |0x01|0x00|0x00|0x00| +----+----+----+----+ ^ | &data

(uint8_t *)&data = 1;

But if your machine is big endian, it will look like the below expression:

+----+----+----+----+ |0x00|0x00|0x00|0x01| +----+----+----+----+ ^ | &data

(uint8_t *)&data = 0;

We can also check the endianness of the machine using the union. We need to create a union that has an integer variable and an array of 4 characters. If the first element (au8DataBuff [0]) of the character array is equal to the LSB Bytes of integer, then the system will be little endian otherwise big-endian.

#include <stdio.h> #include <inttypes.h> typedef union { //integer variable uint32_t u32RawData; //array of character uint8_t au8DataBuff[4]; } RawData; int main(void) { RawData uCheckEndianess; //assign the value uCheckEndianess.u32RawData = 1; //check the array first index value if (uCheckEndianess.au8DataBuff[0] == 1) { printf("little-endian"); }//check the array first index value else if (uCheckEndianess.au8DataBuff[0] == 0) { printf("big-endian"); } return 0; }

C Program to convert little endian to big endian and big endian to little endian:

We can convert little endian to big endian or vice versa using the C programs. So let us see few ways to convert one endian to another.

#include <stdio.h> #include <inttypes.h> //Function to change one endian to another uint32_t ChangeEndianness(uint32_t u32Value) { uint32_t u32Result = 0; u32Result |= (u32Value & 0x000000FF) << 24; u32Result |= (u32Value & 0x0000FF00) << 8; u32Result |= (u32Value & 0x00FF0000) >> 8; u32Result |= (u32Value & 0xFF000000) >> 24; return u32Result; } int main() { uint32_t u32CheckData = 0x11223344; uint32_t u32ResultData =0; //swap the data u32ResultData = ChangeEndianness(u32CheckData); //converted data printf("0x%x

",u32ResultData); return 0; }

Output:

0x44332211

We can also write a Macro to covert one endian to another endian.

#include <stdio.h> #include <inttypes.h> //Macro to swap the byte #define CHANGE_ENDIANNESS(A) ((((uint32_t)(A) & 0xff000000) >> 24) \ | (((uint32_t)(A) & 0x00ff0000) >> 8) \ | (((uint32_t)(A) & 0x0000ff00) << 8) \ | (((uint32_t)(A) & 0x000000ff) << 24)) int main() { uint32_t u32CheckData = 0x11223344; uint32_t u32ResultData =0; u32ResultData = CHANGE_ENDIANNESS(u32CheckData); printf("0x%x

",u32ResultData); return 0; }

Output:

0x44332211

Using the union we can also change the endianness of data.

#include <stdio.h> #include <inttypes.h> typedef union { uint32_t u32RawData; uint8_t au8DataBuff[4]; } RawData; uint32_t ChangeEndianness(uint32_t u32Value) { RawData uChangeData,uOrginalData; uOrginalData.u32RawData = u32Value; //change the value uChangeData.au8DataBuff[0] = uOrginalData.au8DataBuff[3]; uChangeData.au8DataBuff[1] = uOrginalData.au8DataBuff[2]; uChangeData.au8DataBuff[2] = uOrginalData.au8DataBuff[1]; uChangeData.au8DataBuff[3] = uOrginalData.au8DataBuff[0]; return (uChangeData.u32RawData); } int main() { uint32_t u32CheckData = 0x11223344; uint32_t u32ResultData =0; u32ResultData = ChangeEndianness(u32CheckData); printf("0x%x

",u32ResultData); return 0; }

Output:

0x44332211

If you want to learn more about the c language, here 10 Free days C video course for you.

How is the endianness affect the code?

When you perform a bit-wise operation on integer then compiler automatically handles the endianness and you don’t need to care about the endianness of the machine. After the bit-wise operation if the machine is little endian the LSB store at lower address either the LSB stored at a higher address.

Mainly endianness affects the result when you perform the typecasting in your program, suppose you are creating a character array of four elements and you need to convert character array in a single integer element then what will be the result? It depends on the endianness of the processor.

#include <stdio.h> #include <inttypes.h> int main() { uint8_t au8RawBuf[4] = {0x01, 0x00,0x00,0x00}; uint32_t u32RawData = *(uint32_t *)au8RawBuf; printf("0x%x

", u32RawData); return 0; }

If you compile this code on a little-endian processor then the output will be 0x01 but if you compiled it on the big-endian processor then the output will be 0x01000000. Endianness also plays a vital role, when you will send the data from the one system to another system across the network. In this situation, you have to swap the data if the endianness of the receiving system different from the transmitting system.

Exchanging Data Between Different Endian Machines

Nowadays every system which is attached to one another is either little endian or big endian. Everything is fine till you haven’t sent any data to the network because if the endianness of transmitting and the receiving system is different then it might cause data loss. So to avoid the culprit, I am describing the ways to how to prevent data loss.

Sent the data in common format

This approach is very simple, whenever we send the data through the network then we don’t know the receiving machine is big-endian or little-endian, so we have to send the data in a common format. The standard network order is big-endian, we can call it “network order”.

There is a lot of function which has used when we send the data to the network.

htons() – “Host to Network Short”

htonl() – “Host to Network Long”

ntohs() – “Network to Host Short”

ntohl() – “Network to Host Long”

The above-mentioned functions help to program in portability no matter if the program runs on a little-endian or big-endian machine, they always work the way they should.

Note: There is no order matter for the single byte.

Let’s see an example for a better understanding.

Suppose there are two machines S1 and S2, S1 and S2 are big-endian and little-endian relatively. If S1(BE) wants to send 0x44332211 to S2(LE), the following scenario occurred.

S1 has the number 0x44332211, it will store in memory as following sequence 44 33 22 11.

S1 calls htonl() as the program has been written to be portable. The number is still represented as 44 33 22 11 and sent over the network.

S2 receives 44 33 22 11 and calls the ntohl().

S2 gets the value represented by 11 22 33 44 from ntohl(), which then results to 0x44332211 as wanted.

Note: I have already explained in little-endian lower bytes stored at the lower address.

Which Endianness is better?

There is no meaning to say who is the better big endian or little endian, it only arranges the data in a predefined order.

Little Endian

In the case of little-endian, you can access the first bytes of data with zero offsets because LSB stored at the lower address. Hence in case of the little-endian relation between address and offset is 1:1 and easy to access the data.

Big Endian

In the case of big-endian MSB byte stored at the lower address, so we can access the MSB byte with zero offsets and check the sign of data without knowing the length of data.

Note: Endianness is not associated with the register that means register is neither big endian and little endian. Register only stores the data in bit format and its leftmost bit is MSB bit and the rightmost bit is LSB bit.

Conclusion

We find there is no advantage of using one endianness over the other, both only define the byte sequence order. Today mostly personal computers and desktop come in little-endian architecture. Endian also do not affect the single-byte it only affects the multi-byte data because we consider byte is the atomic unit from the storage point of view.

Your opinion matters

Although here I have tried to discuss a lot of points regarding the little endian vs big endian. I would like to know your opinion on the system’s endianness and little endian vs big endian. So please don’t forget to write a comment in the comment box.

Recommended Post





