为什么用%d打印字符变量会在c中给出负值?
我尝试了以下代码,希望输出为正64:为什么用%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",
char val = 0x80;
printf("%d",val>>1);
我对发生的事情的理解是(如果我可能是错的,请纠正我):
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
进行签名或不签名。)Inchar 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
的签名性无关