一个变量所具有的两个属性,一是类型,二是内容。而类型决定了这个变量的在内存中开辟空间的大小,不同的类型对应的存储空间不同,那么计算机是如何存储这些变量的呢?
首先,我们知道计算机中的符号数有三种表示方法。分别为源码、反码、补码:
- 源码 将数据按照二进制的正负数形式翻译成二进制序列
- 反码 源码的符号为不变,其余二进制位取反
- 补码 反码加一
注:对于正数,其源码、反码、补码相同
对于整形来说:数据存放在内存中的数据就是补码
-
大小端概念
- 大端存储模式:数据的低位保存在内存的高地址,高位保存在内存的低地址
- 小端存储模式:数据的低位保存在内存的低地址,高位保存在内存的高地址
设计小程序来判断当前机器的大小端
#include<stdio.h>
#include<stdlib.h>
int check_sys1()
{
int i=1;
char * p = (char*)&i;
return * p;
}
int check_sys2()
{
union
{
int i ;
char a;
}un;
un.i = 1;
return un.a;
}
int main()
{
int ret = check_sys2();
if (ret== 1)
{
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是一个有符号的字符型数据,而且是负数,所以补码就是 11111111,但a在输出的时候按"%d"格式输出,所以就需要整型提升,提升时候看什么呢?看原生类型,也就是a定义的类型,因为a是有符号的且为负数,所以就补1,变成11 11 11 11。在输出的时候对a进行解码,结果就是-1,b,c照着这个思路进行就可得出结果。
2.
#include <stdio.h>
#include<stdlib.h>
int main()
{
char a = -128;
printf("%u\n", a);
system("pause");
return 0;
}
这个题和第一个不同的地方在于计算它的补码时超过其类型(char)大小,你需要明白,当cpu将内存中的值读取到cpu时,允许超过其类型大小,理解这点,分析过程和第一题一样。
3.
#include <stdio.h>
#include<stdlib.h>
int main()
{
char a = 128;
printf("%u\n", a);
system("pause");
return 0;
}
a正数的最大值只能取到127,表明初始化超范,但这并不影响我们分析,我们可以不考虑其数值大小,直接将其当成一个普通数据,但是当发生整形提升时,有符号a在计算机中存储为9位(0 1000 0000),但是这个时候你不能把它真正的符号位当作提升时看的符号位,char大小为8位,计算机读取时认为它的第八位就是最高位,对于有符号数来说也就是它的符号位,第二题也存在这种问题。总结一下,对于此类问题,保持一个原则,即看它当前类型的最高位
4.
int main()
{
char a[1000];
int i;
for(i=0; i<1000; i++)
{
a[i] = -1-i;
}
printf("%d",strlen(a));
return 0;
}
这个问题的关键有两点,数组a的元素类型是char,范围为-128到127,所以循环1000次肯定会造成范围溢出;再一个就是要明白,strlen的结束标志;这两点明白了,这个题目分析起来也就顺畅了。存储的元素-1到-128,等到负的最大值时,下一个数是多少呢?如果前面的问题你明白了,那么这里显然就是127,不解释!接着存储127-1。这就是数组a中存储的所有元素。
- 注:有符号数正数的最大值比负数的最小值绝对值差1;无符号和有符号最大值加1都等于最小值(最大值、最小值指的是这个类型的取值范围)
5.
#include <stdio.h>
unsigned char i = 0;
int main()
{
for(i = 0;i<=255;i++)
{
printf("hello wolrd\n");
}
return 0;
}
死循环!
通过上面题目的分析,大家对于整型提升也有了一个清晰的认识。这类问题关键在于你对数据在内存中的存储的理解,理解到位了,这些问题都是纸老虎:)