整型
要理解整型是如何在内存中存储,我们必须先认识原码、反码和补码。
正数的原码补码反码一样
原码:按二进制翻译,最高位为符号位,0代表正数,1代表负数。
在计算机中只有加法没有减法,减一个数相当于加一个负数。
如果我们用原码来计算 例:2+2 正数加正数
0000 0010 + 0000 0010 = 0000 0100 =4 没有问题
如果我们用来计算负数: 2+ (-2) 正数加负数
0000 0010 + 1000 0010 = 1000 0100 = -4
看来用原码计算是有问题的,既然只能用加法来计算,我们考虑把负数除符号位全部
取反(补码),再来计算试一试
补码:符号位不变,其余位置按位取反.
0000 0010 + 1111 1101(即-2)=1111 1111
再把1111 1111取反回来 为 1000 0000 = - 0 这样似乎 2 + (-2)= 0,虽然得到的是-0。
再试一试其他的数相加 2 + -3
0000 0010 +1111 1100 = 1111 1110(补码)=1000 0001(原码) = -1 没有问题
再试一试 -2+ -2
1111 1101 +1111 1101 = 1111 1010(补码)= 1000 0101(原码)= -5 不对
再试一试 -3 + -1
1111 1100 +1111 1110 = 1111 1010 (补码)=1000 0101(原码)= -5 不对
发现用反码计算正数+负数时结果正确,负数+负数时不正确,于是出现了补码。
补码:负数的补码等于他的原码自低位向高位,尾数的第一个‘1’及其右边的‘0’保持不变,左边的各位按位取反,符号位不变。 这里刚好与反码+1相同
至于为何这样这里不作讨论,补码的出现使计算机可以通过加法来实现减法运算。
数据中的存储
int i = 10,int类型32位i的二进制补码如下
00000000 00000000 00000000 00001010
监视 i 的内存
可以看出,此计算机是小端模式,十六进制的0a对应二进制的1010十进制的10。
监视 j 的内存
内存中的数都是以补码的形式存储的 -20的补码如下
11111111 11111111 11111111 11101100
ff ff ff ec
六进制中的ec对应二进制中的11101100;ff对应二进制中的11111111 。
练习
1.输出结果为a = -1,b = -1,c = 255
int main()
{
char a = -1;
signed char b = -1;
unsigned char c = -1;
printf("a = %d,b=%d,c = %d", a, b, c);
system("pause");
return 0;
}
分析:在visual 中char和signed char都是有符号类型,unsigned char是无符号类型,char类型为一个字节,而%d为4个字节,所以要进行整型提升,整型提升时看自身类型。
char和signed char都为有符号类型,a和b 整型提升
11111111-------->>>11111111 11111111 11111111 11111111 以%d(有符号整型十进制)输出 为-1
注意:这里如果用%u输出,结果会不同,下面例子中会具体分析。
unsigned为无符号类型,c整型提升,整型提升时看自身类型
11111111----->>>>00000000 00000000 00000000 11111111 以%d输出为255。
2.输出结果为4294967168
int main()
{
char a = -128;
printf("%u\n", a);
return 0;
}
分析:a在内存中的存储为十六进制的80 转换为二进制
1000 0000
a以%u类型输出,需要整型提升,看a自身类型char,为有符号类型,前面24位补1
11111111 11111111 11111111 1000 0000
再以%u类型输出,因为%u为无符号类型十进制输出,故结果为 4294967168。
把-128改成128
int main()
{
char a = 128;
printf("%u\n", a);
return 0;
}
a的二进制没变,输出结果也不会变。
int main()
{
int i = -20;
unsigned int j = 10;
printf("%u\n", i + j);//按照补码的形式进行运算,最后格式化成为有符号整数
system("pause");
return 0;
}
i 与 j 以补码形式相加
结果为
11111111 11111111 11111111 11110110
再将 11111111 11111111 11111111 11110110以%u无符号输出。
4
int main()
{
unsigned int i;
for (i = 9; i >= 0; i--)
{
Sleep(1000);
printf("%u\n", i);
}
system("pause");
return 0;
}
运行结果如下
分析:当 i = 0时,其在内存中的存储如下
此时的二进制为
00000000000000000000000000000000
i --后
此时的二进制为
11111111111111111111111111111111
因为i为无符号类型,最高位不是符号位,故一直满足i>=0的条件
再按%u无符号类型输出,得到以上结果。
如果将%u换成%d结果会怎么样呢?
分析:换成%d只是输出会有影响,i在内存中的存储是不会有变化的。
当 i=0,i-- 后,i在内存中的存储为
11111111111111111111111111111111以%d输出结果为-1.
继续,i–,其在内存中的存储为
11111111111111111111111111111110以%d输出为-2.
继续,i–,
11111111111111111111111111111101以%d输出为-3
…
结果如下
对应%d输出的-1和%u输出的429497295,他们的输出对象i在内存中的存储都是
11111111 11111111 11111111 11111111 ,只是以不同方式输出结果不同罢了。
5
int main()
{
char a[1000];
int i;
for (i = 0; i<1000; i++)
{
a[i] = -1 - i;
}
printf("%d", strlen(a));
return 0;
}
分析:
char型数组,a[i] = -1-i ;i ++,因此数组中应该依次存放
-1 -2 -3 -4 -5 …但是char类型存储范围是-128~127,超出这个范围,数据溢出。
再来看看输出
strlen记录字符串长度,以’\0’结束,此题中即遇到数组中的‘0’结束
所以这里打印的是数组中字符‘0’,前面一共有多少个数。
先看看数组中的存储情况
我们可以看到,从-128开始变为127随后一次递减一直到0。
分析:从i = 128开始,arr[i]= -1 -i,因为数组类型char存储范围为-128~127,因此此时会发生数据的溢出。
-1 - 128 = -129
-129对应二进制补码形式:
1 0111 1111,因为char一个字节,只能存储8位,故存在arr[i] 中的为 0111 1111即 127
继续
-1 - 129 = -130
1 0111 1110 存储在arr [i] 中的为 0111 1110 即 126
依次…直到0
最终结果为255.
6
int main()
{
unsigned char i = 0;
for (i = 0; i <= 255; i++)
{
printf("hello world\n");
}
system("pause");
return 0;
}
分析:死循环,i = 255时,i++溢出,i =0。