我知道在网上使用gcc byteswap和其他替代方法可以解答这个问题的答案,但我想知道为什么我的代码不能正常工作。
首先我有gcc警告(我觉得不应该来)但我之所以不想使用byteswap是因为我需要确定我的机器是大端还是小端并使用byteswap相应i。,e如果我的机器是大端,我可以记住字节,没有任何翻译,否则我需要交换它们并复制它。
static inline uint64_t ntohl_64(uint64_t val)
{
unsigned char *pp =(unsigned char *)&val;
uint64_t val2 = ( pp[0] << 56 | pp[1] << 48
| pp[2] << 40 | pp[3] << 32
| pp[4] << 24 | pp[5] << 16
| pp[6] << 8 | pp[7]);
return val2;
}
int main()
{
int64_t a=0xFFFF0000;
int64_t b=__const__byteswap64(a);
int64_t c=ntohl_64(a);
printf("\n %lld[%x] [%lld] [%lld]\n ", a, a, b, c);
}
Warnings:-
In function \u2018uint64_t ntohl_64(uint64_t)\u2019:
warning: left shift count >= width of type
warning: left shift count >= width of type
warning: left shift count >= width of type
warning: left shift count >= width of type
Output:-
4294901760[00000000ffff0000] 281470681743360[0000ffff00000000] 65535[000000000000ffff]
我在一台小端机器上运行它,所以byteswap和ntohl_64应该产生完全相同的值,但不幸的是我得到了完全意想不到的结果。如果有人可以指出什么是错误的话会很棒。
答案 0 :(得分:2)
您的代码不起作用的原因是因为您正在转移unsigned char
。当它们移位时,位从顶部下降,任何大于7的移位都可以作为返回0(尽管由于机器代码移位的方式,一些实现最终会产生奇怪的结果,x86就是一个例子)。您必须将它们转换为您想要的最终尺寸的首字母:
((uint64_t)pp[0]) << 56
使用gcc的最佳解决方案是使用htobe64。这个功能可以帮到你。
P.S。这有点偏离主题,但是如果你想让函数在字节序上可移植,你可以这样做:
根据Nova Denizen的评论进行编辑:
static inline uint64_t htonl_64(uint64_t val)
{
union{
uint64_t retVal;
uint8_t bytes[8];
};
bytes[0] = (val & 0x00000000000000ff);
bytes[1] = (val & 0x000000000000ff00) >> 8;
bytes[2] = (val & 0x0000000000ff0000) >> 16;
bytes[3] = (val & 0x00000000ff000000) >> 24;
bytes[4] = (val & 0x000000ff00000000) >> 32;
bytes[5] = (val & 0x0000ff0000000000) >> 40;
bytes[6] = (val & 0x00ff000000000000) >> 48;
bytes[7] = (val & 0xff00000000000000) >> 56;
return retVal;
}
static inline uint64_t ntohl_64(uint64_t val)
{
union{
uint64_t inVal;
uint8_t bytes[8];
};
inVal = val;
return bytes[0] |
((uint64_t)bytes[1]) << 8 |
((uint64_t)bytes[2]) << 16 |
((uint64_t)bytes[3]) << 24 |
((uint64_t)bytes[4]) << 32 |
((uint64_t)bytes[5]) << 40 |
((uint64_t)bytes[6]) << 48 |
((uint64_t)bytes[7]) << 56;
}
假设编译器在返回的过程中没有对uint64_t做某事,并假设用户将结果视为8字节值(而不是整数),则该代码应该适用于任何系统。运气好的话,如果你是在一个大端系统上,你的编译器将能够优化整个表达式,并且如果你在一个小端机器上使用一些内置的字节交换技术(并且保证它仍然适用于任何其他一种机器)。
答案 1 :(得分:2)
uint64_t val2 = ( pp[0] << 56 | pp[1] << 48
| pp[2] << 40 | pp[3] << 32
| pp[4] << 24 | pp[5] << 16
| pp[6] << 8 | pp[7]);
pp[0]
是unsigned char
,56
是int,因此pp[0] << 56
执行左移unsigned char
,unsigned char
结果。这不是你想要的,因为你希望所有这些转换的类型都是unsigned long long。
解决此问题的方法是投射,例如((unsigned long long)pp[0]) << 56
。
答案 2 :(得分:1)
由于pp[x]
为8位宽,因此表达式pp[0] << 56
导致零。您需要对原始值进行显式屏蔽,然后转移:
uint64_t val2 = (( val & 0xff ) << 56 ) |
(( val & 0xff00 ) << 48 ) |
...
在任何情况下,只需使用编译器内置函数,它们通常会产生单个字节交换指令。
答案 3 :(得分:0)
CastH和移位工作如PlasmaHH建议,但我不知道为什么32位移位自动转换而不是64位。
typedef uint64_t __u64;
static inline uint64_t ntohl_64(uint64_t val)
{
unsigned char *pp =(unsigned char *)&val;
return ((__u64)pp[0] << 56 |
(__u64)pp[1] << 48 |
(__u64)pp[2] << 40 |
(__u64)pp[3] << 32 |
(__u64)pp[4] << 24 |
(__u64)pp[5] << 16 |
(__u64)pp[6] << 8 |
(__u64)pp[7]);
}