Can someone help me understand this code?
int reverse_endianess(int value) {
int resultat = 0;
char *source, *destination;
int i;
source = (char *) &value;
destination = ((char *) &resultat) sizeof(int);
for (i = 0; i < sizeof(int); i )
*(--destination) = *(source );
return resultat;
}
I can't understand this part of code:
destination = ((char *) &resultat) sizeof(int);
for (i = 0; i < sizeof(int); i )
*(--destination) = *(source );
CodePudding user response:
The following causes destination
to point to the byte that follows resultat
(as long as resultat
is an int
):
destination = ((char *) &resultat) sizeof(int);
It could also have been written as follows:
destination = (char *)(&resultat 1);
The following is just a simple memory copy loop:
for (i = 0; i < sizeof(int); i )
*(--destination) = *(source );
It's equivalent to the following:
for (i = 0; i < sizeof(int); i ) {
--destination; // Point to the one byte earlier.
*destination = *source; // Copy one byte.
source ; // Point to one byte later.
}
Program flow (assuming 32-bit int
and 8-bit char
)
After setup:
source value
---------- --- --- --- ---
| ------- | a | b | c | d |
---------- | --- --- --- ---
| ^
------
destination resultat
---------- --- --- --- ---
| ------- | 0 | 0 | 0 | 0 |
---------- | --- --- --- ---
| ^
----------------------
After one pass of the loop:
source value
---------- --- --- --- ---
| ------- | a | b | c | d |
---------- | --- --- --- ---
| ^
----------
destination resultat
---------- --- --- --- ---
| ------- | 0 | 0 | 0 | a |
---------- | --- --- --- ---
| ^
------------------
When it's done:
source value
---------- --- --- --- ---
| ------- | a | b | c | d |
---------- | --- --- --- ---
| ^
----------------------
destination resultat
---------- --- --- --- ---
| ------- | d | c | b | a |
---------- | --- --- --- ---
| ^
------
CodePudding user response:
Three different approaches. The first one is most efficient on the systems having byte reversing instructions.
#define SWAPUC(a,b) do{unsigned char temp = (a); (a) = (b); (b) = temp;}while(0)
int reverse(int i)
{
unsigned int val = i;
if(sizeof(val) == 4)
val = ((val & 0xff) << 24) | ((val & 0xff00) << 8) | ((val & 0xff0000) >> 8) | ((val & 0xff000000) >> 24);
if(sizeof(val) == 8)
val = ((val & 0x00000000000000ffULL) << 56) | ((val & 0xff00000000000000ULL) >> 56) |
((val & 0x000000000000ff00ULL) << 40) | ((val & 0x00ff000000000000ULL) >> 40) |
((val & 0x0000000000ff0000ULL) << 24) | ((val & 0x0000ff0000000000ULL) >> 24) |
((val & 0x00000000ff000000ULL) << 8) | ((val & 0x000000ff00000000ULL) >> 8);
return val;
}
int reverse1(int val)
{
union
{
unsigned i;
unsigned char uc[sizeof(val)];
}uni = {.i = val};
if(sizeof(val) == 8)
{
SWAPUC(uni.uc[7], uni.uc[0]);
SWAPUC(uni.uc[6], uni.uc[1]);
SWAPUC(uni.uc[5], uni.uc[2]);
SWAPUC(uni.uc[4], uni.uc[3]);
}
if(sizeof(val) == 4)
{
SWAPUC(uni.uc[3], uni.uc[0]);
SWAPUC(uni.uc[2], uni.uc[1]);
}
return uni.i;
}
int reverse2(int val)
{
unsigned char uc[sizeof(val)];
memcpy(uc, &val, sizeof(uc));
if(sizeof(val) == 8)
{
SWAPUC(uc[7], uc[0]);
SWAPUC(uc[6], uc[1]);
SWAPUC(uc[5], uc[2]);
SWAPUC(uc[4], uc[3]);
}
if(sizeof(val) == 4)
{
SWAPUC(uc[3], uc[0]);
SWAPUC(uc[2], uc[1]);
}
memcpy(&val, uc, sizeof(uc));
return val;
}
int main(void)
{
printf("%x\n", reverse2(0xaabbccdd));
}
The generated code (x86):
reverse:
mov eax, edi
bswap eax
ret
reverse1:
mov eax, edi
xor edx, edx
mov ecx, edi
shr eax, 24
movzx esi, ch
sal ecx, 24
mov dl, al
mov eax, edi
sal esi, 16
shr eax, 16
mov dh, al
movzx eax, dx
or eax, esi
or eax, ecx
ret
reverse2:
mov eax, edi
xor edx, edx
mov ecx, edi
shr eax, 24
movzx esi, ch
sal ecx, 24
mov dl, al
mov eax, edi
sal esi, 16
shr eax, 16
mov dh, al
movzx eax, dx
or eax, esi
or eax, ecx
ret
.LC0:
.string "%x\n"
Or cortex M4 (this one has byte swapping instruction)
reverse:
rev r0, r0
bx lr
reverse1:
mov r3, r0
lsrs r2, r3, #24
movs r0, #0
bfi r0, r2, #0, #8
ubfx r2, r3, #16, #8
bfi r0, r2, #8, #8
ubfx r2, r3, #8, #8
bfi r0, r2, #16, #8
bfi r0, r3, #24, #8
bx lr
reverse2:
mov r3, r0
lsrs r2, r3, #24
movs r0, #0
bfi r0, r2, #0, #8
ubfx r2, r3, #16, #8
bfi r0, r2, #8, #8
ubfx r2, r3, #8, #8
bfi r0, r2, #16, #8
bfi r0, r3, #24, #8
bx lr
.LC0:
So the winner is the first function using only the bitwise arithmetics.
CodePudding user response:
Lets say sizeof(int) = 4
so 32 bit int.
A char is sizeof 1, 1 byte.
A normal int* looks like this:
aabbccdd // the int in hexadecimal
^ pointer points to start
If we cast it to a char*, we get aa
. Thats whats done with source
.
If we now add sizeof 4, we jump 4 bytes to the right:
aabbccdd??
^
We are now one byte behind the value, accessing this may segfault the program or just read garbage. This does not happen due to the use of --destination
instead of destination--
. It is decremented first.
Now we just read the integer passed in from the front, while writing it from the back:
a1b2c3d4 // original int
->
d4c3b2a1 // destination
<-
Note that two hexadecimal digits are one byte, which is why we dont get
4d3c2b1a
. We leave the bytes in the correct way, but put the first bytes last.
CodePudding user response:
I was using this for a very long time.
data is a pointer to value to be reversed
n is the number of char to be reversed; usually 2, 4, 8 for short, int, long long.
But this can be different on various architectures/OS
void SwapEndianN(char *data, unsigned short n) {
unsigned short k; char c;
for ( k=0 ; k < (n/2) ;k ) {
c = *(data ((n-1)-k));
*(data ((n-1)-k)) = *(data k);
*(data k) = c;
}
}