C语言中的逻辑右移与~0值

10

我有一段非常简单的代码,在其中使用逻辑移位时,对于 ~0 的值会以奇怪的方式工作。

据我所知,这与有符号/无符号数据类型有关。

#include <stdio.h>

void printfbits(int x) {
    for (int i=7; i>=0;i--) {
        printf("%d", x>>i & 1);
    }
    printf("\n");
}

int main() {
    printfbits(~0>>1); 
}

我期望的是0111111,而不是1111111。 我也尝试过了,但没有成功。

printfbits(((unsigned int)~0)>>1);

3
int类型是32位而不是8位。 - Alex F
1
好的!我已经尝试了char,现在它可以工作了! - delkov
3
@AlexF C中的int并不一定是32位!它们可以是任何大于16位的大小。现在常见的大小为16、32和64位。 - marcelm
4个回答

7

在大多数平台上,int类型的长度为32位或64位。因此,如果向右移动超过8位,那么你将拥有超过8位的启用状态:

11...11111111 >> 1

变成:

11...11111111 // if sign extension happens
01...11111111 // if not

正如您所看到的,无论是否发生符号扩展,您仍然会看到所有的“1”,因为您只打印了低8位。

4

负数的右移是实现定义的。对于gcc,会向右移入1,否则会向右移入0。

你在类型转换方面的想法是正确的,但由于函数仍然需要一个int类型的参数,所以这并没有帮助。你需要将函数改为接受一个unsigned char类型的参数,并在函数调用中执行位掩码以屏蔽除最低字节以外的所有位。

#include <stdio.h>

void printfbits(unsigned char x) {
    for (int i=7; i>=0;i--) {
        printf("%d", x>>i & 1);
    }
    printf("\n");
}

int main() {
    printfbits((~0u & 0xff)>>1);
}

此外,注意常量0的后缀U。这使得该常量的类型为unsigned int

虽然C标准确实说当第一个操作数为负数时,>>的行为是实现定义的,但我向你挑战,找到一个现代的C实现,它默认情况下不将其定义为算术移位。标准中的语言基本上是古老的编译器和使用其他方式表示有符号整数而不是二进制补码的古老CPU的遗物。 - Ilmari Karonen
@IlmariKaronen:如果有人有一些影响力,认真努力承认所有在20世纪90年代末就基本标准化的有用事物,那将是有帮助的,然而编译器编写者决定未定义行为意味着“跳出轨道,即使执行环境会定义一个有用的行为”。我曾经测试过一个编译器,在这个编译器中,有符号右移和无符号右移被处理成相同的方式;在我的抱怨(也许还有其他测试者的抱怨)之后,供应商认识到,即使这种行为是允许的,处理器可以更快地处理它... - supercat
在某些情况下,符号扩展行为绝对是二进制补码平台上更可取的选择,而不是进行符号扩展移位。有趣的是,C99标准决定将负数左移从大多数实现中严格定义为在所有情况下都是未定义行为,即使C99对于64位或更长的无符号类型的要求使得C99在除了二进制补码平台之外的任何地方都难以实现,在那里通常只会有一个明智的行为。 - supercat

1
~0中的否定将使用类型int发生。但是,即使您执行~(unsigned char)0,由于隐式提升,它仍将使用类型int。因此,您将在左侧获得额外的1位(int通常为32位大(必须至少为16))。您可以通过将位否定结果转换为uint8_t来去除它们。
我还建议对unsigned进行位运算(0u而不是0),因为这些的语义更好地标准化了。
#include <stdio.h>
#include <stdint.h>

void printfbits(int x) {
    for (int i=7; i>=0;i--) {
        printf("%d", x>>i & 1);
    }
    printf("\n");
}

int main() {
    printfbits( (uint8_t)~0u >>1 );  //prints 01111111
}

0

这是有符号位移,其实现定义不同。但在大多数二进制补码计算机上:

enter image description here


不考虑符号扩展,假设“int”比8位长,OP仍将看到所有的“1”。 - Acorn

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