问题
I found some code in a built in library that I have and need to extend.
But it appears to be broken:
#define BSWAP16(__x) ((((__x) >> 8) | ((__x) << 8)))
Does not function the same as:
__builtin_bswap16()
This program proves it.
#include <stdio.h>
#define BSWAP16(__x) ((((__x) >> 8) | ((__x) << 8)))
int main(int argc, char* argv[])
{
unsigned short a = (unsigned short)BSWAP16(0xff00);
unsigned short b = __builtin_bswap16(0xff00);
short c = (short)BSWAP16(-8);
short d = __builtin_bswap16(-8);
printf("a=%04x, b=%04x, c=%04x, d=%04x\n", a,b,c,d);
return 0;
}
Output:
a=00ff, b=00ff, c=ffffffff, d=fffff8ff
I'm not wanting answers telling me I should use endian.h or __builtin_bswap16. On the target platform used by this in-house library on this platform/compiler configuration, I'm triggering this default code that is defaulting to using the above macro.
So my question is. Why doesn't it work for negative numbers?
If I represent -8 as a short value of 0xfff8 it works.
So I'm guessing it has something to do with internal conversion to int.
How do I fix this macro to work properly?
回答1:
It is undefined behavior to left shift a negative number, from the draft C99 standard section 6.5.7
Bitwise shift operators which says (emphasis mine going forward):
The result of E1 << E2 is E1 left-shifted E2 bit positions; vacated bits are filled with zeros. If E1 has an unsigned type, the value of the result is E1 ´ 2E2, reduced modulo one more than the maximum value representable in the result type. If E1 has a signed type and nonnegative value, and E1 ´ 2E2 is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined.
So the result of the left shift of -8
is unpredictable. Casting to unsigned short should fix the issue:
BSWAP16((unsigned short)-8)
-8
is an integer constant(literal) and since it does not have a suffix will be an int since int can take on it's value. Assuming 32-bit
int and twos complement will have have the following value:
FFFFFFF8
casting to unsigned short will remove the unwanted higher bits. Casting to unsigned int won't help since it will preserve the higher bits.
Also right shifting a negative number is implementation defined:
The result of E1 >> E2 is E1 right-shifted E2 bit positions. If E1 has an unsigned type or if E1 has a signed type and a nonnegative value, the value of the result is the integral part of the quotient of E1 / 2E2. If E1 has a signed type and a negative value, the resulting value is implementation-defined.
回答2:
You should mask out the unwanted bits before shifting.
#define BSWAP16(__x) (((((__x) & 0xFF00) >> 8) | (((__x) & 0xFF) << 8)))
Regarding why it doesn't work without shifting, note that -8
is indeed 0xFFFFFFF8
. Without masking, there are plenty of higher 1
bits. int
is used during computation.
来源:https://stackoverflow.com/questions/24565837/why-doesnt-this-swap-macro-using-shifts-not-work-for-negative-numbers