C中的右左移位

时间:2017-01-29 10:54:11

标签: c bit-manipulation

我对C中的位移做了一个小测试,所有的位移0,8,16位都没问题,我理解发生了什么。

但32位右移或左移对我来说并不清楚,我正在测试的变量是32位长。

然后,我改变了32位变量,这些变量将保持移位结果,但是32位左右移位是相同的!

这是我的代码:

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <inttypes.h>

int main() {
    uint32_t code = 0xCDBAFFEE;

    uint64_t bit32R = code >> 32;
    uint16_t bit16R = code >> 16;
    uint8_t bit8R = code >> 8;
    uint8_t bit0R = code >> 0;

    uint64_t bit32L = code << 32;
    uint16_t bit16L = code << 16;
    uint8_t bit8L = code << 8;
    uint8_t bit0L = code << 0;

    printf("Right shift:\nbit32R %.16x\nbit16R %x\nbit8R %x\nbit0R %x\n\n",
           bit32R, bit16R, bit8R, bit0R);
    printf("Left shift:\nbit32L %.16x\nbit16L %x\nbit8L %x\nbit0L %x\n\n",
           bit32L, bit16L, bit8L, bit0L);
}

这是我得到的结果:

Right shift:
bit32R 00000000cdbaffee
bit16R 0
bit8R cdba
bit0R ff

Left shift:
bit32L 00000000cdbaffee
bit16L 0
bit8L 0
bit0L 0

Process returned 61 (0x3D)   execution time : 0.041 s
Press any key to continue.

3 个答案:

答案 0 :(得分:6)

将整数右移一个等于或大于其大小的位数是不确定的行为。

  

C11 6.5.7按位移位运算符

     

语法

shift-expression: additive-expression
    shift-expression << additive-expression
    shift-expression >> additive-expression
     

约束

     

每个操作数都应具有整数类型。

     

语义

     

对每个操作数执行整数提升。结果的类型是提升的左操作数的类型。如果右操作数的值为负或大于或等于提升的左操作数的宽度,则行为未定义。

     

E1 << E2的结果是E1左移E2位位置;腾出的位用零填充。如果E1具有无符号类型,则结果的值为E1×2 E2 ,比结果类型中可表示的最大值减少一个模数。如果E1具有带符号类型和非负值,并且E1×2 E2 在结果类型中可表示,那么这就是结果值;否则,行为未定义。

     

E1 >> E2的结果是E1右移E2位位置。如果E1具有无符号类型或者E1具有有符号类型和非负值,则结果的值是E1 / 2 E2 的商的整数部分。如果E1具有签名类型和负值,则结果值是实现定义的。

平台上int的大小似乎最多为32位,因此bit32Rbit32L的初始化程序具有未定义的行为。

应写入64位表达式:

uint64_t bit32R = (uint64_t)code >> 32;

uint64_t bit32L = (uint64_t)code << 32;

此外,printf中使用的格式对于传递的参数不正确(除非int具有64位,这将产生不同的输出)。

您的编译器似乎不完全符合C99,您应该在函数return 0;的主体末尾添加最后的main()语句。

以下是更正后的版本:

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <inttypes.h>

int main(void) {
    uint32_t code = 0xCDBAFFEE;

    uint64_t bit32R = (uint64_t)code >> 32;
    uint16_t bit16R = code >> 16;
    uint8_t bit8R = code >> 8;
    uint8_t bit0R = code >> 0;

    uint64_t bit32L = (uint64_t)code << 32;
    uint16_t bit16L = code << 16;
    uint8_t bit8L = code << 8;
    uint8_t bit0L = code << 0;

    printf("Right shift:\n"
           "bit32R %.16"PRIx64"\n"
           "bit16R %"PRIx16"\n"
           "bit8R %"PRIx8"\n"
           "bit0R %"PRIx8"\n\n",
           bit32R, bit16R, bit8R, bit0R);

    printf("Left shift:\n"
           "bit32L %.16"PRIx64"\n"
           "bit16L %"PRIx16"\n"
           "bit8L %"PRIx8"\n"
           "bit0L %"PRIx8"\n\n",
           bit32L, bit16L, bit8L, bit0L);

    return 0;
}

输出结果为:

Right shift:
bit32R 0000000000000000
bit16R cdba
bit8R ff
bit0R ee

Left shift:
bit32L cdbaffee00000000
bit16L 0
bit8L 0
bit0L ee

这可能不是您所期望的,因为变量的类型有些不一致。

答案 1 :(得分:1)

一个问题是您使用%x打印64位整数。您应该为每个变量使用正确的格式说明符。有这方面的宏:

#define __STDC_FORMAT_MACROS
#include <inttypes.h>

// ...

printf("64 bit result: %" PRIx64 "\n", bit32R);
printf("16 bit result: %" PRIx16 "\n", bit16R);
printf("8 bit result: %" PRIx8 "\n", bit8R);

可以找到更多信息here

答案 2 :(得分:0)

你没有在那里进行64位左移,因为代码是uint32_t,所以编译器使用32位版本的运算符。此外,您应该告诉print使用long long(与uint64_t相同)

#include <cstdint>
#include <stdio.h>
int main ()
{
uint32_t code = 0xCDBAFFEE;


uint64_t bit32R=((uint64_t)code)>>32;
uint16_t bit16R=code>>16;
uint8_t bit8R=code>>8;
uint8_t bit0R=code>>0;

uint64_t bit32L=((uint64_t)code)<<32;
uint16_t bit16L=code<<16;
uint8_t bit8L=code<<8;
uint8_t bit0L=code<<0;


printf("Right shift:\nbit32R %llx\nbit16R %x\nbit8R %x\nbit0R %x\n\n", bit32R,bit16R,bit8R,bit0R);
printf("Leftt shift:\nbit32L %llx\nbit16L %x\nbit8L %x\nbit0L %x", bit32L,bit16L,bit8L,bit0L);
}

结果是:

Right shift:
bit32R 0
bit16R cdba
bit8R ff
bit0R ee

Leftt shift:
bit32L cdbaffee00000000
bit16L 0
bit8L 0
bit0L ee

如果你有C99兼容的编译器,你应该使用inttypes.h中定义的宏,遗憾的是某些平台没有这些定义。 printf的格式描述符与平台有关。