文字常量和具有相同值的变量之间左移的执行差异

Rya*_*sen 2 c types bit-manipulation

我正在编写一个C函数,它接受一个参数n并返回一个int,其位表示为n 1,后跟足够的0来填充数据类型(总共32位).我的代码目前看起来像这样:

int upperBits(int n) {
    int retval = 0 - 1;
    int shift = 32 - n;
    retval = retval << shift;
    return retval;
}
Run Code Online (Sandbox Code Playgroud)

当n = 0时,此代码失败,返回值为-1,由32 1表示,而不是0.但是,我将shift替换为文字:

int upperBits(int n) {
    int retval = 0 - 1;
    int shift = 32 - n;
    retval = retval << 32;
    return retval;
}
Run Code Online (Sandbox Code Playgroud)

代码正常工作,返回0.我使用print语句验证当n = 0调用函数时shift = 32,所以我不明白为什么这些行为不同.造成这种差异的原因是什么,我该如何规避呢?

如果它是相关的,则代码在Linux机器上运行并使用gcc编译.我需要使用只有这些运算符的直线代码:! ˜ & ˆ | + << >>

编辑:我仍然不知道究竟是什么问题,或一个优雅的解决方案,但这种解决方法是有效的:

int upperBits(int n) {
        int retval = 0 - 1;
        int shift = 32 - n;
        int isnull = !(n);
        printf ("%x %x %x \n", retval, shift, n);
        retval = retval << (shift - isnull);
        retval = retval << isnull;
        printf ("%x %x %x \n", retval, shift, n);
        return retval;
}
Run Code Online (Sandbox Code Playgroud)

dbu*_*ush 5

你正在进行非法的左移.

左移一个负数会调用未定义的行为,就像移动大于或等于所讨论类型的位宽的量一样.

关于按位移位运算符的C标准的第6.5.7节规定:

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

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

您可以使用无符号类型并通过检查班次的大小来更正此问题:

uint32_t upperBits(int n) {
    uint32_t retval = 0xffffffff;
    if (n <= 0 || n > 32) {
        return 0;
    } else {
        int shift = 32 - n;
        retval = retval << shift;
        return retval;
    }
}
Run Code Online (Sandbox Code Playgroud)