字节反转整数的问题

时间:2010-09-19 12:20:15

标签: c endianness

Godday all。

有人可以解释这两个字节反转函数实现的逻辑差异。

示例1:

uint32_t byte_reverse_32(uint32_t num) {
    static union bytes {
        uint8_t b[4];
        uint32_t n;
    } bytes;
    bytes.n = num;

    uint32_t ret = 0;
    ret |= bytes.b[0] << 24;
    ret |= bytes.b[1] << 16;
    ret |= bytes.b[2] << 8;
    ret |= bytes.b[3];

    return ret;
}  

示例2:

uint32_t byte_reverse_32(uint32_t num) {
    static union bytes {
        uint8_t b[4];
        uint32_t n;
    } bytes;
    bytes.n = num;

    uint32_t ret = 0;
    ret |= (bytes.b[0] << 24) || (bytes.b[1] << 16) || (bytes.b[2] << 8) || (bytes.b[3]);
    return ret;
}

我必须遗漏一些东西,因为对于无符号整数0xFE12ADCF,第一个示例正确地给出0xCFAD12FE,而第二个示例产生1。我错过了什么?

顺便说一句,我无法弄清楚如何获得'&lt;&lt;&lt;在pre + code-tags中,因此LSHIFT。如果可以这样做,请随时编辑(并评论如何=))。

感谢。

编辑:修正了bytes - 从未分配过的变量。

8 个答案:

答案 0 :(得分:10)

|||不是同一个运算符。第一个是按位OR,这是你想要的。第二个是布尔OR,这就是你所拥有的。

答案 1 :(得分:3)

应该注意的是,通过混合物理级内存访问和值级按位运算来整数字节反转整个方法看起来非常可疑。我的意思是,它可能适合你,但为什么有人这样做呢?创造这种奇怪混合的重点是什么?

如果您决定通过将整数值重新解释为4字节序列来求助于直接物理内存访问,那么自然反转方法就是交换字节0和3并交换字节1和2。 / p>

uint32_t byte_reverse_32(uint32_t num) {
    union bytes {
        uint8_t b[4];
        uint32_t n;
    } bytes;
    uint8_t t;

    bytes.n = num;

    t = bytes.b[0]; bytes.b[0] = bytes.b[3]; bytes.b[3] = t;
    t = bytes.b[1]; bytes.b[1] = bytes.b[2]; bytes.b[2] = t;

    return bytes.n;
}

替代方法是通过按位运算实现所有内容而不进行任何内存重新解释,从而在值级别完成整个事情。

uint32_t byte_reverse_32(uint32_t num) {
    uint32_t res;

    res = num & 0xFF; num >>= 8; res <<= 8;
    res = num & 0xFF; num >>= 8; res <<= 8;
    res = num & 0xFF; num >>= 8; res <<= 8;
    res = num & 0xFF; 

    return res;
}

如果正确实施,上述两种方法都可以移植,因为它们可以在大端和小端平台上运行。人们通常会转向值级别的按位方法,专门用于通过联合来避免物理内存重新解释,因为内存重新解释几乎总是一种黑客攻击。

但你现在拥有的东西(即两种方法混合在一起)似乎没什么意义(如果有的话)。你现在拥有的东西会在小端平台上执行逆转,但在大端平台上什么都不做。我知道你可能根本不会对大端平台感兴趣,所以这个问题对你来说并不重要。但是,两种替代(通常是互斥)技术的混合对我来说看起来很奇怪。

同样,如果您已经决定使用基于联合的方法,只需交换字节并完成它。

答案 2 :(得分:1)

您根本没有使用num参数,而是使用未初始化的bytes联合变量。

此处并不真正需要联合,您只需以可以访问其字节的方式强制转换num参数:

uint8_t *b = (uint8_t*)&num;
// then access b[0] through b[3]    

正如其他人已经回答的那样,使用按位|运算符代替||

答案 3 :(得分:1)

您可能希望使用系统标头(glibc中的endian.h,BSD上的sys/endian.hmachine/endian.h来定义bswap_32(或者名称略有不同)为您的建筑。有些CPU有一条指令,比比特混洗更快。

即如果您已经拥有32位值或正在从内存中读取对齐的单词。如果您正在读取未对齐的32位值,那么在读取它们时按正确的顺序组装字节比首先读取然后交换更快。

答案 4 :(得分:0)

你正在使用||,它是一个布尔运算符,如果两个参数中的一个与零不同,则为true(1)。

要获得正确的结果,您需要使用按位OR运算符,就像您在第一个示例中所做的那样(x | = y是x = x | y而不是x = x || y)。

所以第二个例子应该与

一起使用
ret |= (bytes.b[0] LSHIFT 24) | (bytes.b[1] LSHIFT 16) | (bytes.b[2] LSHIFT 8) | (bytes.b[3]);

希望有所帮助。

答案 5 :(得分:0)

您正在使用||这是逻辑或运算符,你想要二进制|操作

答案 6 :(得分:0)

这个问题已经回答了,但只有我的2美分关于可以进入生产的代码:

在代码中使用static关键字,如下所示:

uint32_t byte_reverse_32(uint32_t num) {
    static union bytes {
        uint8_t b[4];
        uint32_t n;
    } bytes;
/* etc.*/

没有任何性能值(或任何值),并且如果从多个线程调用,将确保您的函数将具有数据竞争(即,它将不安全使用)。

删除static:你的功能会一样快,多线程安全(不提简单)。

uint32_t byte_reverse_32(uint32_t num) {
    union bytes {
        uint8_t b[4];
        uint32_t n;
    } bytes;
/* etc.*/

答案 7 :(得分:0)

完美的工作!注意,这里int是4个字节。 输入0xAABBCCDD 输出0xDDCCBBAA

#include

int main()
{

    int A= 0x12ABCDEF;
    int R=0;
    R = R | ((A&0x000000ff) << 24);
    R = R | ((A&0x0000ff00) << 8);
    R = R | ((A&0x00ff0000) >> 8);
    R = R | ((A&0xff000000) >>24);

    printf("A is %x  R is %x \n", A, R);

    return 0;
}