这里写目录标题
- 整型数据在内存中的存储
- 预备知识
- 原码、反码、补码
- 大小端介绍
- 定义:
- 为什么要有大小端?
- 设计一个程序判断大小端
- 针对整型数据存储的练习题
- 1、此代码段输出结果是?
- 分析
- 2、求出下面程序的输出结果
- 分析
- 4、求程序输出结果
- 分析
- 5、继续求程序输出结果
- 分析
- 6、求程序输出结果
- 分析
- 7求程序段结果
- 分析
整型数据在内存中的存储
预备知识
原码、反码、补码
计算机中的有符号数有三种表示方式,即原码、反码和补码
三种表示方法各不相同。
原码:直接将二进制按照正负数的形式翻译成二进制即可
反码:原码符号位不变,其余位取反
补码:反码+1
正数的原码反码补码相同
大小端介绍
定义:
大端(存储)模式:数据的低位保存在内存的高地址中,数据的高位保存在内存的低地址中
小端(存储)模式:数据的低位保存在内存的低地址中,数据的高位保存在内存的高地址中
为什么要有大小端?
在计算机系统中,以字节为单位,每个地址单元都对应着一个字节,一个字节为8bit,对于位数大于8位的处理器来说,由于寄存器宽度大于一个字节,那么必然存在着一个如何将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式。
设计一个程序判断大小端
int check_sys()
{
int i = 1;
return (*(char*)&i);//首先取到i的地址,强转为char*,再对其解引用
}
int main()
{
int ret = check_sys();
if (ret)
{
printf("小端\n");
}
else
{
printf("大端\n");
}
system("pause");
return 0;
}
针对整型数据存储的练习题
1、此代码段输出结果是?
#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;
}
分析
a,b,c三个变量的初始化值都为-1;
-1的补码:1111 1111 1111 1111 1111 1111 1111 1111
对于a和b来说,char类型和signed char类型一样,故而输出的结果也一样。
首先,内存中存储的是-1的补码,由于char类型只占一个字节,故而在写入内存是发生截断,内存中存储的为二进制序列1111 1111
。
输出函数以“%d”
的形式打印,而从内存中取出来的数据为1111 1111
,所以要发生整形提升,比特位由原来的8位提升到32位,整型提升时高位补1还是补0需要看自身类型,a,b为有符号数,故而补1。
所以此时二进制序列变为1111 1111 1111 1111 1111 1111 1111 1111
,而又因为数据是按照“%d”(有符号数)打印,看最高位为1,所以说这个二进制序列为结果的补码,我们将其转换为原码后结果为:1000 0000 0000 0000 0000 0000 0000 0001
,对应我们十进制的-1,所以,a,b的输出结果均为-1。
对于变量c来说,它的类型为unsigned char,根据前面的思路,数据在写入内存时进行了一样的操作,而从内存取数据时就有所差异了!
取出来的数据依旧是二进制序列1111 1111
,发生整形提升时高位补1还是 补0需要看自身类型,由于c是无符号数,所以补0。
也就是说,此时的二进制序列从为 0000 0000 0000 0000 0000 0000 1111 1111
,又因为数据是按照“%d”(有符号数)打印,看最高位为0,所以此时的二进制序列就是对应的结果,也就是十进制的255。
综上所述,输出的结果是:a=-1,b=-1,c=255
2、求出下面程序的输出结果
#include <stdio.h>
int main()
{
char a = -128;
char b = 128;
printf("%u\t%u\n",a,b);
return 0;
}
分析
-128 的补码为:1111 1111 1111 1111 1111 1111 1000 0000
128的补码为: 0000 0000 0000 0000 0000 0000 1000 0000
将该数据写入内存时,由于a,b的类型均为 char,所以会发生截断,只将1000 0000
存入a中,1000 0000
存入b中。
取数据时:将二进制序列1000 0000
从内存中取出,由于要以“%u”的形式输出,所以需要对a和b都要做整形提升。
由于a,b的自身类型为char,有符号类型且1000 0000
高位为1,所以高位补1,形成新的二进制序列为1111 1111 1111 1111 1111 1111 1000 0000
输出函数以“%u”输出,为无符号类型,所以新的二进制序列即为最终结果!对应十进制的4,294,967,168
4、求程序输出结果
int i = -20;
unsigned int j = 10;
printf("%d\n",i+j);
分析
按照补码的形式进行计算,最后格式化为有符号整数
-20的补码为:1111 1111 1111 1111 1111 1111 1110 1100
,
10的补码为:0000 0000 0000 0000 0000 0000 0000 1010
二者相加的二进制序列为:1111 1111 1111 1111 1111 1111 1111 0110
以“%d”输出,二进制序列符号位为1,说以是负数,说明此二进制序列为最终结果的补码,所以将其进行转换,最终结果为:1000 0000 0000 0000 0000 0000 0000 1010
,对应十进制的-10。
相应地,这里也间接说明,两个类型不一致的数据做运算时,要发生隐式类型准换,只有类型一致时,才可以进行预算。
5、继续求程序输出结果
unsigned int i;
for (i = 9; i >= 0; i--)
{
printf("%u\n",i);
}
分析
对于此题来说,首先,变量i的类型是无符号整型数据,for循环中的条件判断为i>=0
,表达式左右两边的类型不一致,所以要发生隐式类型转换。
当i逐次递减,减到0的时候,下一次理应为-1,此时从内存中读取出来i的二进制序列为:1111 1111 1111 1111 1111 1111 1111 1111
,但是i的类型为无符号整型数据,所以此二进制序列就是最终结果,也就是说此时i的值变成了unsigned int 的最大值;也就是说,每次i递减到0的时候,下一次就会变成unsigned int 的最大值。
综上,此程序代码为死循环,具体输出结果是:
9 8 7 6 5 4 3 2 1 0 4294967295 …0 4294967295…0无限循环。
6、求程序输出结果
int main()
{
char a[1000];
int i;
for(i=0; i<1000; i++)
{
a[i] = -1-i;
}
printf("%d",strlen(a));
return 0;
}
分析
数组a的容量为1000,每一个元素类型为char,for 循环做的操作是对数组的 每一个单元重新赋值。最后输出的值是strlen(a)
stelen():求字符串长度,以‘\0’(实际存储为数字0)作为结束标志,不包括‘\0’在内。
也就是说,此题的主要问题在于循环经历多少次之后,对数组单元的赋值为0???
我们知道,char类型的数据取值范围为[-128~127],也就是说,只要 -1-i 的值在此范围内,就是合法的。
i = 0---->a[i] = -1
i = 1---->a[i] = -2
以此类推
i=127------>a[i] = -128、
i = 128----->a[i] = -129??? 很明显出错了,超出了char的范围。
-129补码:1111 1111 1111 1111 1111 1111 0111 1111
由于数组类型为char类型,故而发生截断,得到新的二进制序列为0111 1111,重新写回数组a时,判断最高位为0,所以此二进制序列就是最终结果,对应十进制的的127.
也就是说,
i = 128 时----->a[i] = 127
由此可以看出,i不断递增的过程中,a[i]的数据内容从-1递减到-128然后由-128变成127,再由127递减到0,
这总共经历了128+128-1(最后一个0不包括在内)长度
所以最终结果为:255
7求程序段结果
#include <stdio.h>
unsigned char i = 0;
int main()
{
for(i = 0;i<=255;i++)
{
printf("hello world\n");
}
return 0;
}
分析
对于循环变量i:它的类型为unsigned char ,取值范围为[0~255]
当i的值为255时,合法,再加1理应为256,对应的原码为1 0000 0000
,补码和原码一致,但是在存入时要发生截断,存入的数据实际为0000 0000
,对应十进制的0
综上所述:此代码段的结果是发生死循环,因为i 的值从0增长到255再变成0,然后再到255。