我在内置库中找到了一些我需要扩展的代码。
但它似乎被打破了:
#define BSWAP16(__x) ((((__x) >> 8) | ((__x) << 8)))
与以下功能不同:
__builtin_bswap16()
这个程序证明了这一点。
#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;
}
输出:
a=00ff, b=00ff, c=ffffffff, d=fffff8ff
我不想回答告诉我应该使用endian.h或__builtin_bswap16。在此平台/编译器配置上此内部库使用的目标平台上,我触发了默认代码,该代码默认使用上述宏。
所以我的问题是。为什么它不适用于负数?
如果我将-8表示为0xfff8的短值,则可以正常工作。
所以我猜测它与内部转换为int有关。
如何修复此宏才能正常工作?
答案 0 :(得分:4)
左移一个负数的未定义行为,来自草案C99标准部分6.5.7
按位移位运算符表示(强调我的前进) :
E1的结果&lt;&lt; E2是E1左移E2位位置;腾空 位用零填充。如果E1具有无符号类型,则值为 结果是E1'2E2,比最大值减少了一个模数 在结果类型中可表示。 如果E1有签名类型和 非负值,E1'2E2可在结果类型中表示, 那就是结果价值; 否则,行为是 未定义。强>
因此-8
左移的结果是不可预测的。转换为 unsigned short 可以解决问题:
BSWAP16((unsigned short)-8)
-8
是一个整数常量( literal ),因为它没有后缀,所以 int 是 int 可以承担它的价值。假设32-bit
int 和二重补充将具有以下值:
FFFFFFF8
转换为 unsigned short 将删除不需要的高位。转换为 unsigned int 将无济于事,因为它将保留更高的位。
同样正确地移动负数是实现定义的:
E1&gt;的结果&gt; E2是E1右移E2位位置。如果E1有 无符号类型或E1具有带符号类型和非负值, 结果的值是E1 /的商的不可分割的一部分 2E2。 如果E1具有带符号类型和负值,则为结果值 是实现定义的。
答案 1 :(得分:2)
你应该在移位之前屏蔽掉不需要的位。
#define BSWAP16(__x) (((((__x) & 0xFF00) >> 8) | (((__x) & 0xFF) << 8)))
关于为什么它没有移动就无法工作,请注意-8
确实是0xFFFFFFF8
。没有屏蔽,有很多更高的1
位。计算期间使用int
。