反转字节顺序的整数出现问题

3

大家好。

请问有谁能够解释一下这两个字节反转函数实现的逻辑区别。

示例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。我错过了什么?
顺便说一下,我无法弄清如何在pre+code标签中获取'<<',因此使用了LSHIFT。如果可以做到,请随意编辑(并评论如何实现)。
谢谢。
编辑:修复了从未分配的bytes变量。

1
只需将代码块缩进四个空格(这就是编辑器中“0101”按钮的作用),然后您就可以使用 << 等内容。 - caf
1
你的任务也不起作用,你应该分配给 bytes.n。但更好的方法是初始化而不是赋值。正如paercebal所说,去掉 static 属性,然后使用 = { .n = num } 初始化变量。 - Jens Gustedt
1
移除虚假的 static 存储说明符,它会使你的代码非常不可重入。static 没有 const 几乎总是一个错误。 - R.. GitHub STOP HELPING ICE
8个回答

10

|不是与||相同的运算符。第一个是按位或,这正是您想要的。第二个是布尔或,这就是您所拥有的。


4
值得注意的是,通过混合物理层内存访问和值级别的位操作来反转整数的整个方法看起来非常可疑。我的意思是,这种方法可能适用于您,但为什么有人要这样做呢?创建这样奇怪的组合的目的是什么?
如果您决定采用直接物理内存访问的方法,将整数值重新解释为4个字节序列,则自然的反转方法就是交换字节0和3,以及交换字节1和2。
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;
}

如果正确实现,上述两种方法都是可移植的,可以在大端和小端平台上使用。人们通常会选择基于值级别的按位操作方法,以避免通过联合进行物理内存重新解释,因为内存重新解释几乎总是一种欺骗。
但是,现在您拥有的功能(即两种方法混合在一起)似乎毫无意义(如果有的话)。现在,它将在小端平台上执行反转操作,但不会在大端平台上执行任何操作。我理解您可能根本不关心大端平台,因此这个问题对您来说并不重要。但是,两种替代方法(通常是互斥的)的混合看起来对我来说相当奇怪。
如果您已经决定使用基于联合的方法,请交换字节并完成它。

1

你可能想要使用系统头文件(在glibc中是endian.h,在BSD上是sys/endian.hmachine/endian.h),它们为你的架构定义了bswap_32(或者稍微不同的名称下相同的函数)。一些CPU有一个指令可以比位移更快地完成这个操作。

如果你已经有了32位的值或者正在从内存中读取一个对齐的字,则可以使用该方法。如果你正在读取一个非对齐的32位值,则在读取时按正确顺序组装字节比先读取再交换更快。


0

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

为了获得正确的结果,您需要位或运算符,即 |,就像您在第一个示例中所做的那样(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]);

希望这有所帮助。

0

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

在这里并不真正需要使用联合体,你可以简单地将 num 参数转换为一种方式,以便可以访问其字节:

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

正如其他人已经回答的那样,使用按位或运算符|而不是||


编辑。匆忙中从记忆中输入=)。 - manneorama

0
问题已经得到解答,但这里有我的两点建议,关于一个可能应用于生产环境的代码:
在你的代码中使用 static 关键字,如下所示:
uint32_t byte_reverse_32(uint32_t num) {
    static union bytes {
        uint8_t b[4];
        uint32_t n;
    } bytes;
/* etc.*/

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

请删除static:您的函数将与static一样快,并且支持多线程(更简单)。

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

0

你正在使用 || 运算符,这是逻辑或运算符,你需要使用二进制 | 运算符。


0

它完美地工作了!请注意这里的int是4字节。 输入0xAABBCCDD 输出0xDDCCBBAA

#包括

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;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接