Printf函数格式化程序
具有以下简单的C++代码:
#include <stdio.h>
int main() {
char c1 = 130;
unsigned char c2 = 130;
printf("1: %+u
", c1);
printf("2: %+u
", c2);
printf("3: %+d
", c1);
printf("4: %+d
", c2);
...
return 0;
}
输出如下:
1: 4294967170
2: 130
3: -126
4: +130
有人能给我解释一下第一行和第三行的结果吗?
我使用的是具有所有默认设置的Linuxgcc
编译器。
解决方案
Achar
为8位。这意味着它可以表示2^8=256个唯一值。uchar
表示0到255,带符号的char
表示-128到127(绝对可以表示任何东西,但这是典型的平台实现)。因此,将130赋给char
超出范围2,当解释为带符号的char
时,该值溢出并将值换行为-126。编译器将130视为整数,并进行从int
到char
的隐式转换。在大多数平台上,int是32位的,符号位是MSB,值130很容易放入前8位,但是然后编译器想要砍掉24位以将其压缩成一个字符。当这种情况发生时,并且您已经告诉编译器您需要一个带符号的字符,那么前8位的MSB实际上表示-128。啊哦!您现在的内存中有这个1000 0010
,当解释为有符号字符时,它是-128+2。我的平台上的Linter对此尖叫着。。
printf
语句中强制转换值来确认这一点,即printf("3: %+d
", (unsigned char)c1);
,您将再次看到130。
您在第一个printf
语句中看到大值的原因是,您将有符号的char
转换为无符号的int
,其中char
已经溢出。计算机首先将char
解释为-126,然后强制转换为不能表示负值无符号int
,因此您将获得有符号int
的最大值并减去126。
2^32-126=4294967170。。宾果游戏
在printf
语句2中,机器所要做的就是添加24个0以达到32位,然后将该值解释为int
。在语句1中,您已经告诉它您有一个带符号的值,所以它首先将其转换为32位-126值,然后将-ve整数解释为无符号整数。再一次,它颠倒了解释最重要位的方式。有2个步骤:
- 带符号的字符被提升为带符号的int,因为您希望使用int。字符(可能是复制的)添加了24位。因为我们看到的是带符号的值,所以某些机器指令会碰巧执行二进制补码,所以这里的内存看起来非常不同。
- 新的有符号整型内存被解释为无符号的,因此计算机查看MSB并将其解释为2^32,而不是升级中发生的-2^31。
char c1 = 130u;
,您可以取消整齐的Linter警告,但是根据上面的逻辑,您仍然会得到相同的垃圾(即隐式转换丢弃了前24位,而符号位无论如何都是零)。我已经提交了一份基于探索此问题的llvm整齐的功能缺失报告(问题42137,如果您真的想关注它)??。
相关文章