noobincoding noobincoding - 3 months ago 11
C Question

Confusion in interpreting 32 bit integer in Big Endian and Little Endian Machines

I am in process of rewriting code from Big Endian Machine to Little Endian machine.

Let's say there is a variable called

a
, which is a 32 bit integer which holds current timestamp(user request's current timestamp).

In Big Endian machine, right now the code is this way:

uint32 a = current_timestamp_of_user_request;
uint8 arr[3] = {0};
arr[0] = ((a >> (8 * 2)) & 0x000000FF);
arr[1] = ((a >> (8 * 1)) & 0x000000FF);
arr[2] = ((a >> (8 * 0)) & 0x000000FF);


Now, when I am writing the same logic for little endian machine, can I use the same code(method a), or should I convert the code this way(let's call this method b)?

uint32 a = current_timestamp_of_user_request;
uint32 b = htonl(a);
uint8 arr[3] = {0};
arr[0] = ((b >> (8 * 2)) & 0x000000FF);
arr[1] = ((b >> (8 * 1)) & 0x000000FF);
arr[2] = ((b >> (8 * 0)) & 0x000000FF);


I wrote this program to verify:

#include<stdio.h>
#include<stdlib.h>


void main() {
long int a = 3265973637;
long int b = 0;
int arr[3] = {0,0,0};

arr[0] = ((a >> (8 * 2)) & 0x000000FF);
arr[1] = ((a >> (8 * 1)) & 0x000000FF);
arr[2] = ((a >> (8 * 0)) & 0x000000FF);

printf("arr[0] = %d\t arr[1] = %d\t arr[2] = %d\n", arr[0], arr[1], arr[2]);

b = htonl(a);

arr[0] = ((b >> (8 * 2)) & 0x000000FF);
arr[1] = ((b >> (8 * 1)) & 0x000000FF);
arr[2] = ((b >> (8 * 0)) & 0x000000FF);

printf("After htonl:\n");
printf("arr[0] = %d\t arr[1] = %d\t arr[2] = %d\n", arr[0], arr[1], arr[2]);

}


Results:

Result with little endian machine:

bgl-srtg-lnx11: /scratch/nnandiga/test>./x86
arr[0] = 170 arr[1] = 205 arr[2] = 133
After htonl:
arr[0] = 205 arr[1] = 170 arr[2] = 194

Result with big endian machine:
arr[0] = 170 arr[1] = 205 arr[2] = 133
After htonl:
arr[0] = 170 arr[1] = 205 arr[2] = 133


Looks like without conversion to big endian order, the same logic(without
htonl()
) gives exact results in filling the array
arr
. Now, can you please answer should I use
htonl()
or not if I want the array to be the same in both little endian and big endian machines(little endian result should be exact as big endian result).

Answer

Your code as originally written will do what you want on both big endian and little endian machines.

If for example the value of a is 0x00123456, then 0x12 goes in arr[0], 0x34 goes in arr[1], and 0x56 goes in arr[2]. This occurs regardless of what the endianness of the machine is.

When you use the >> and & operators, they operate on the value of the expression in question, not the representation of that value.

When you call htonl, you change the value to match a particular representation. So on a little endian machine htonl(0x00123456) will result in the value 0x56341200. Then when you operate on that value you get different results.

Where endianness matters is when the representation of a number using multiple bytes is read or written as bytes, i.e. to disk, over a network, or to/from a byte buffer.

For example, if you do this:

uint32_t a = 0x12345678;
...
write(fd, &a, sizeof(a));

Then the four bytes that a consists of are written to the file descriptor (be it a file or a socket) one at a time. A big endian machine will write 0x12, 0x34, 0x56, 0x78 in that order while a little endian machine will write 0x78, 0x56, 0x34, 0x12.

If you want the bytes to be written in a consistent order then you would first call a = htonl(a) before calling write. Then the bytes will always be written as 0x12, 0x34, 0x56, 0x78.

Because your code operates on the value and not the individual bytes of the value, you don't need to worry about endianness.

Comments