下面这段代码的输出结果是什么?
#include <stdio.h>
int main()
{
char a = -1;
signed char b = -1;
unsigned char c = -1;
printf("a=%d, b=%d, c=%d", a, b, c);
return 0;
}
我先把运行结果放下来(我的是VS2019环境)
结果 a = -1 、 b = -1 、 c = 255,那为什么呢?
这里就涉及整型提升、数据在内存中的存储,原码、反码、补码的知识,我前面的文章有提到数据的存储、已经原码、反码、补码的知识,这里不在详细解释。
下面是原码、反码、补码的转换方法:
回归开始的题,先从 a 开始吧, a = -1 是负数,所以原码、反码、补码如下:
10000000000000000000000000000001 -1原码
11111111111111111111111111111110 -1反码
11111111111111111111111111111111 -1补码
因为 a 是 char 类型只能存放 8 个 bit 位,所以发生数据截断,取低位的 8 个 bit 位。
11111111 ——— 截取后的 a (补码)
但是 a 以 %d 整型(32 个 bit 位)打印,要发生整型提升(提升后还是补码)
又因为 a 是 char 类型, 我的 VS环境下,把 char 当做 signed 对待,所以 a 最高位是符号位,整型提升的话,要补符号位,如下:
11111111111111111111111111111111 —— 提升后的 a (补码)
提升后的二进制数依然是补码,因为数据在内存中存的就是补码,打印出来的话要转为原码:
11111111111111111111111111111111 —— 补码并且是有符号的
10000000000000000000000000000000 —— 反码(符号位不变,其它位按位取反)
10000000000000000000000000000001 —— 原码(反码+1)
所以 a = -1
因为 b 是 signed(有符号),那么与 a 的方法相同,所以 b = -1。
下来就是 c ,那么 c 跟前面 a、b 一样先写出原码、反码、补码,如下:
10000000000000000000000000000001 —— -1原码
11111111111111111111111111111110 —— -1反码
11111111111111111111111111111111 —— -1补码
因为 c 是 char 类型只能存放8个bit位,又要截断也是取低位:
11111111 —— 截取后的 c
这里 c 也是以 %d 整型(32个bit位)打印,打印的话也要发生整型提升(提升后还是补码)
但是这里 c 是 unsigned (无符号的char)整型提升的话与前面不同,无符号提升的话,高位补 0,不补符号位!
00000000000000000000000011111111 —— 提升后的 c
因为 c 最高位是 0,所以是正数,正数的原码、反码、补码相同!
所以 c = 255
发生整型提升是根据要被提升的数的类型提升的。
比如 a 是char,是根据 signed 提升的,我的 VS 环境下根据 signed 提升的。
b 是 signed, 所以 b 直接根据有符号规则提升。
但是 C 语言中我们通常用的类型 char ,它究竟是被当做 signed 型还是 unsigned 型,由编译器决定的。
而 c 是 unsigned 类型,直接根据无符号规则提升。
有符号提升:根据最高位也就是符号位提升,补的是符号位。
无符号提升:高位全补 0。
第二题:
#include <stdio.h>
int main()
{
char a1 = -128;
printf("%u\n", a1);
char a2 = 128;
printf("%u\n", a2);
return 0;
}
第三题:
#include <stdio.h>
int main()
{
char a[1000];
int i;
for(i = 0; i < 1000; i++)
{
a[i] = -1-i;
}
printf("%d\n", strlen(a));
return 0;
}
第四题:
#include <stdio.h>
unsigned char i = 0;
int main()
{
for( i = 0; i <=255; i++)
{
printf("Hello word\n");
}
return 0;
}
这些题的做法与第一题做法相似,牵扯整型提升、数据在内存中的存储(原码、反码、补码)的知识,可以试着挑战一下。