0

我知道使用 gcc byteswap 和网络上的其他替代方法可以解决这个问题,但我想知道为什么下面的代码不起作用。

首先,我有 gcc 警告(我觉得不应该出现),我不想使用字节交换的原因是因为我需要确定我的机器是大端还是小端并相应地使用字节交换,例如,如果我的机器是大端的,我可以按原样 memcpy 字节而不需要任何翻译,否则我需要交换它们并复制它。

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

4 回答 4

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是一个整数,所以pp[0] << 56执行左移作为unsigned char一个unsigned char结果。这不是您想要的,因为您希望所有这些转变都具有 unsigned long long 类型。

解决此问题的方法是强制转换,例如((unsigned long long)pp[0]) << 56.

于 2012-10-01T15:34:44.110 回答
2

您的代码不起作用的原因是因为您正在转移unsigned chars. 当它们移位时,位会从顶部下降,任何大于 7 的移位都可以视为返回 0(尽管由于机器代码移位的工作方式,某些实现最终会产生奇怪的结果,x86 就是一个例子)。您必须将它们转换为您希望最终尺寸首先达到的任何尺寸:

((uint64_t)pp[0]) << 56

您使用 gcc 的最佳解决方案是使用 htobe64。此功能为您完成一切。

PS这有点离题,但如果你想让函数跨字节序可移植,你可以这样做:

根据 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 字节值(而不是整数),那么该代码应该可以在任何系统上运行。运气好的话,如果你在一个大端系统上,你的编译器将能够优化整个表达式,如果你在一个小端系统上,你的编译器将能够使用一些内置的字节交换技术(并且保证它仍然可以在任何其他机器上工作一种机器)。

于 2012-10-01T15:22:05.410 回答
1

由于pp[x]是 8 位宽,因此表达式的pp[0] << 56结果为零。您需要对原始值进行显式屏蔽,然后进行移位:

uint64_t val2 = (( val & 0xff ) << 56 ) |
                (( val & 0xff00 ) << 48 ) |
                ...

在任何情况下,只需使用编译器内置程序,它们通常会产生单个字节交换指令。

于 2012-10-01T15:15:03.860 回答
0

铸造和移位工作如 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]);
}
于 2012-10-01T15:36:55.220 回答