c ++ 64位网络到主机转换

时间:2012-10-01 15:09:14

标签: c++ network-programming

我知道在网上使用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应该产生完全相同的值,但不幸的是我得到了完全意想不到的结果。如果有人可以指出什么是错误的话会很棒。

4 个答案:

答案 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 char56是int,因此pp[0] << 56执行左移unsigned charunsigned 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]);
}