在本文中,我会举例一些有关整形数据存储的典型的例题,并详细讲解。
阅读本文前,建议阅读一下之前的知识点部分博客:
【c语言】内存的世界,数据何以为家(一)—整形篇
- 题目一
#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;
}
对于 signed char b = -1,我们这样理解:
1.对于char型,我们可以先假设为普通整形数据,写出补码
10000000 00000000 00000000 00000001
11111111 11111111 11111111 11111110
11111111 11111111 11111111 11111111
2.由于char型只占8个bit,所以对上面的补码进行截断,只留下后8位
11111111
3.由于以“%d”输出,所以我们将补码进行整形提升
11111111 11111111 11111111 11111111
4.所以我们得到了整形下的a的补码,还原为原码,再还原位十进制为-1
对于 unsigned char c = -1,我们这样理解:
1.前两步与signed char b 相同,截断留下后八位补码
11111111
2.由于以“%d”输出,所以我们将补码进行整形提升
00000000 00000000 00000000 11111111
3.所以我们得到了整形下的a的补码,补码与原码一致,还原位十进制为255
2^7 + 2^6 + 2^5 + 2^4 + 2^3 + 2^2 + 2+1 = 255
看到这里,大家可能会有一些疑问:
1.整形提升是什么,为啥有时候补0,有时候补1?整型提升是指在输出或者计算变量时,变量大小不够整型(int型)的大小,则需要把变量的大小提升至整型再进行输出或者计算。
对于有符号数:如果符号位为1,则补1,否则补0
对于无符号数:补02.char类型算作是signed char还是unsigned呢?
其实在c语言中,并没有明确规定,所以看编译器而定,在vs环境中,可以
看到,a与b的结果相同,所以vs中char默认为signed char.ps: int 是signed int 还是unsigned int ? signed int
- 题目二
#include<stdio.h>
int main()
{
char a = -128;
printf("%u\n", a);//以无符号整形%u打印
printf("%d\n", a);//以有符号整形%d打印
return 0;
}
对于以无符号整形%u打印的情况:
1.与上题类似,求补码,再截断,再整形提升,不再赘述。
10000000 00000000 00000000 100000000 -128的原码
11111111 11111111 11111111 011111111 反码
11111111 11111111 11111111 100000000 补码
10000000 char截断
11111111 11111111 11111111 100000000 整形提升
2.提升后,以无符号整形%u打印,此时认为没有符号位的概念了
那么该数就是正数了,补码等于原码。
11111111 11111111 11111111 100000000
3.将原码转化为十进制,得到 4294967168,很大的一个数。
对于以有符号整形%d打印的情况:
1.与上题类似,求补码,再截断,再整形提升,不再赘述。
10000000 00000000 00000000 100000000 -128的原码
11111111 11111111 11111111 011111111 反码
11111111 11111111 11111111 100000000 补码
10000000 char截断
11111111 11111111 11111111 100000000 整形提升
2.把得到的补码转回原码
11111111 11111111 11111111 011111111 反码
10000000 00000000 00000000 100000000 原码
3.转化为10进制:-128
- 题目三
#include<stdio.h>
int main()
{
char a = 128;
printf("%u\n", a);
return 0;
}
//得到结果:4294967168
1.求补码,再截断,再整形提升
00000000 00000000 00000000 100000000 //原反补相同
10000000 //截断
11111111 11111111 11111111 100000000 //整形提升
2.提升后,以无符号整形%u打印,此时认为没有符号位的概念了
那么该数就是正数了,补码等于原码。
11111111 11111111 11111111 100000000
3.将原码转化为十进制,得到 4294967168,很大的一个数。
//与练习二得到的结果相同
- 题目四
#include<stdio.h>
int main()
{
int i = -20;
unsigned int j = 10;
printf("%d\n", i + j);
printf("%u\n", i + j);
}
在之前的博客中讲过,我们使用补码进行计算:
1.分别计算得到i,j的补码:
10000000 00000000 00000000 00010100
11111111 11111111 11111111 11101011
11111111 11111111 11111111 11101100 i的补码
00000000 00000000 00000000 00001010 j的补码
2.将补码相加
11111111 11111111 11111111 11110110 补码相加后
3.
3.1若以%d形式打印,则将首位1看作符号位,求原码:
11111111 11111111 11111111 11110101 反码
10000000 00000000 00000000 00001010 原码
转化为十进制:-10
3.2若以%u形式打印,此时没有符号位的概念了,那么
该数就是正数了,补码等于原码:
得到一个很大的数字:4294967286
- 题目五
#include<stdio.h>
int main()
{
unsigned int i;
for (i = 9; i >= 0; i--)
{
printf("%u\n", i);
}
}
//运行结果:死循环
这道题很简单,因为很显然,无符号型整形的值恒大于等于0,所以i>=0的循环条件无效,
程序陷入死循环
- 题目六
#include<stdio.h>
#include<string>
int main()
{
char a[600];
int i;
for (i = 0; i < 600; i++)
{
a[i] = -1 - i;
//printf("%d ", a[i]);
}
printf("%d", strlen(a));
return 0;
}
这里的结果看上去很奇怪,其实这与char类型变量的取值范围有关。
char类型的取值范围(十进制)是 -128 ~ 127
char类型变量在内存中占8个bit位,内存中补码由00000000 -> 11111111;
其中00000000 ->01111111 对应十进制为1 -> 127
而从10000000 ->11111111 对应十进制为-128 -> -1
- 题目七
#include<stdio.h>
int main()
{
unsigned char i = 0;
for (i = 0; i <= 255; i++)
{
printf("hello world\n");
}
}
这题在知道char类型数据的取值范围后就很明显了:
对于无符号char型,同理,从00000000 ->11111111,无符号位,
所以直接可以算出十进制为0 ->255。所以恒小于等于255,故死循环。
- 题目八
int main()
{
unsigned char a = 200;
unsigned char b = 100;
unsigned char c = 0;
c= a+b;
printf("%d, %d ",a+b,c);
return 0;
}
a 的二进制是 11001000
b 的二进制的 01100100
当a+b的时候,需要进行整形提升,相当于两个 int 相加
00000000 00000000 00000000 11001000 +
00000000 00000000 00000000 01100100
= 00000000 00000000 00000001 00101100
但是 c显然放不下,需要进行截断: 00101100
所以 a+b=300 ,c=44
- 题目九
在 32 位大端处理器上 变量b等于_______.
int main()
{
unsigned int a = 0x1234;
unsigned char b = *(unsigned char*)&a;
}
答案:0x00