为什么用%d打印字符变量会在c中给出负值?

为什么用%d打印字符变量会在c中给出负值?,c,bit-manipulation,placeholder,signed,shift,C,Bit Manipulation,Placeholder,Signed,Shift,我尝试了以下代码,希望输出为正64: char val = 0x80; printf("%d",val>>1); 我对发生的事情的理解是(如果我可能是错的,请纠正我): 参考ASCII表,没有0x80到任何字符的映射,因此我假设它存储为无符号整数 按位格式表示为1000 0000,因此右移1将导致0100 0000 当打印为整数值时,将显示为正64 但它显示的是-64 相比之下: char val = 0x40; printf("%d",

我尝试了以下代码,希望输出为正64:

char val = 0x80; 
printf("%d",val>>1);
我对发生的事情的理解是(如果我可能是错的,请纠正我):

  • 参考ASCII表,没有0x80到任何字符的映射,因此我假设它存储为无符号整数
  • 按位格式表示为1000 0000,因此右移1将导致0100 0000
  • 当打印为整数值时,将显示为正64
  • 但它显示的是-64

    相比之下:

    char val = 0x40; 
    printf("%d",val>>1);
    
    给出正32


    值是否在第一种情况下隐式转换为有符号整数,而在第二种情况下不隐式转换为有符号整数?

    有符号整数的右移是由实现定义的。在大多数现代系统中,有符号整数是2的补码,移位将由编译器转换为算术移位

    移位后,
    val
    的二进制值为
    0xc0
    ,在二者的补码编码中为
    -64

    val
    首先转换为有符号整数,然后传递给函数。如果你在你的问题上投入一些精力,并在代码中添加几行代码,你就会自己发现它

    int main(void)
    {
        char c = 0x80;
    
        printf("%d\n", c >> 1);
        printf("%x\n", c >> 1);
        printf("%hhd\n", c >> 1);
        printf("%hhx\n", c >> 1);
    
        c >>= 1;
    
        printf("%d\n", c);
        printf("%x\n", c);
        printf("%hhd\n",c);
        printf("%hhx\n",c);
    }
    


    您还可以看到MSB位是否为0算术移位的行为与二进制移位的行为完全相同,因此
    0x40>>1==0x20
    您的C实现使用八位带符号的
    char
    。(C标准允许对
    char
    进行签名或不签名。)In
    char val=0x80
    ,一个
    字符
    不能代表您初始化它时使用的值128。在这种情况下,值128被转换为
    char
    ,根据C 2018 6.3.1.3 3,该值产生实现定义的值或陷阱。您的实现可能会产生−128(这是一个常见的结果,因为二进制中的128是10000000,将超出范围的结果转换为8位2的补码整数通常只需将值的低位8位重新解释为8位2的补码。在2的补码中,10000000表示−128.)

    因此
    val>>1
    要求移位−128右一位。根据C 2018 6.5.7 5,将负值右移产生实施定义的值。生产−64是一个常见的结果

    (具体而言,在
    val>>1
    中,
    val
    自动从
    char
    提升到
    int
    。它具有相同的值,−128.然而,如果使用32位的
    int
    ,它将被表示为11111111110000000而不是10000000。然后右移“算术地”传播符号位,得到1111111100000,即−有些C实现可能会“逻辑”右移,将符号位设置为零,产生011111000000。在这种情况下,
    printf
    将显示“2147483584”,即231−64)


    ASCII是否有代码为0x80的字符无关紧要。无论使用何种字符编码方案,C规则都适用于所涉及的值。

    如果在实现中对
    char
    进行了签名,则值
    (int)(char)0x80
    更像是二进制
    11111111111111111111111000000
    ,因此我假设它存储为无符号整数。-很可能,您正在使用的
    char
    和隐式
    int
    都不是无符号的。如果您想要无符号行为,请明确地说。
    char
    的类型不会根据您在其中存储的内容而改变。如果
    char
    被签名,那么
    0x80
    也被签名,它是
    -128
    。如果
    char
    被签名,那么
    val>>1
    要么是未定义的,要么是实现定义的(不记得是哪个),但是返回到输出
    -64
    ,则您的
    char
    不能取消签名。当传递到变量函数(如
    printf()
    )时,
    char
    类型被提升为
    int
    。它也被提升到
    int
    以执行移位。由于带符号的
    int
    可以保存
    char
    signed char
    unsigned char
    的所有可能值,因此转换后的值为
    int
    类型,而与
    char
    的签名性无关