我们知道数据在内存中都是以2进制的形式存储的;比如int,char,double,float这些类型的数据都是以2进制的形式去存储的,那么这些数据又是如何去存入/取出的呢?
前面我们知道,整数分为有符号整数和无符号整数;而整数在内存中存储有三种表示方式,分别是原码,反码和补码;三种表示方式均分为符号位和数值位;
原码:直接将数值按照正负数的形式翻译成⼆进制得到的就是原码。
反码:将原码的符号位不变,其他位依次按位取反就可以得到反码。
补码:反码+1就得到补码。
对于有符号的整数来说原码,反码和补码是要进行计算的,而对于无符号的整数来说原码,反码和补码来说都相同;下面我们来一一介绍:
有符号整数
对于有符号整数来说,分为正数和负数;
正数的原码,反码,补码相同;
负数的原码,反码和补码要进行计算;
举例说明:
对于有符号整数来说最高位是符号位,0表示正数,1表示负数;后面31位表示数值位也就是有效位;
无符号整数
无符号整数的32个比特位都表示有效位;
举例说明:
我们在观察内存的时候,不难发现我们观察到的数据储存是倒着存的;
比如:
我们发现是先存44 33 22 11;这种倒着存放数据的也就是我们的小端字节序存储;
什么是大端,什么又是小端呢?
大端存储:是指数据的低位字节内容保存在内存的⾼地址处,⽽数据的⾼位字节内容,保存在内存的低地址处。
小端存储:是指数据的低位字节内容保存在内存的低地址处,⽽数据的⾼位字节内容,保存
在内存的⾼地址处。
这个不难理解,
判断当前机器上大端还是小端存储;
可以用指针来,也可以用联合体来表示,联合体的方法可以看前面的联合体介绍;
int main()
{
int a = 1;
char* pa = (char*)& a;
if (*pa)
{
printf("小端\n");
}
else
{
printf("大端\n");
}
return 0;
}
#include
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;
}
题目解析:
这里出现了一个词,整形提升;
如果我们要打印一个将一个char或者short以int类型去打印的话,我们是不足32个比特位的(char 8bit)(short 16bit),那么我们就要补齐剩下的位,那么这里也分2种情况;
如果是无符号整形的话,直接补0即可;
如果是有符号整形,按自己的符号位往上补;也就是我们上面的那种情况;
接下来我们继续:
#include
int main()
{
char a = -128;
printf("%u\n",a);
return 0;
}
题目解析:
#include
int main()
{
char a = 128;
printf("%u\n",a);
return 0;
}
题目解析:
我们发现不管是有符号的char,还是无符号的char这貌似是一个循环;
所以这一题可以显而易见的看出我们存进去的依旧是-128;
那么打印的结果就是跟上一题一样;
#include
int main()
{
char a[1000];
int i;
for(i=0; i<1000; i++)
{
a[i] = -1-i;
}
printf("%d",strlen(a));
return 0;
}
题目解析:
#include
unsigned char i = 0;
int main()
{
for(i = 0;i<=255;i++)
{
printf("hello world\n");
}
return 0;
}
题目解析:
首先我们先来看一段代码:
#include
int main()
{
int n = 9;
float *pFloat = (float *)&n;
printf("n的值为:%d\n",n);
printf("*pFloat的值为:%f\n",*pFloat);
*pFloat = 9.0;
printf("num的值为:%d\n",n);
printf("*pFloat的值为:%f\n",*pFloat);
return 0;
}
对于这个答案我们很疑惑;
这就涉及了浮点数在内存中的存放和取出的规则了,下面一起来了解一下浮点数的存放和取出的方式;
根据国际标准IEEE(电⽓和电⼦⼯程协会) 754,任意⼀个⼆进制浮点数V可以表⽰成下⾯的形式:
V = (−1) ^s∗ M ∗ 2^E
• (−1)S 表⽰符号位,当S=0,V为正数;当S=1,V为负数
• M 表⽰有效数字,M是⼤于等于1,⼩于2的
• 2^E 表⽰指数位
如果我们要把它写成上面那种形式的话就是:
IEEE 754规定:
对于32位的浮点数,最⾼的1位存储符号位S,接着的8位存储指数E,剩下的23位存储有效数字M(float)
对于64位的浮点数,最⾼的1位存储符号位S,接着的11位存储指数E,剩下的52位存储有效数字M(double)
前面我们知道,M是大于等于1,小于2的,通常写成1.xxxxxxxx,所以再存浮点数的有效数字M的时候不会去存小数点前面的1,只会存小数点后面的数,等取出来的时候再放个1放前面;
对于指数E来说,它是一个无符号的整数,但是指数E是可能为负数的,比如0.5,此时指数E为-1;所以IEEE 754规定,在存入E的时候加上一个中间数,如果是4位浮点数的话就加上127,如果是8位浮点数的话就加上1023;。⽐如,2^10的E是10,所以保存成32位浮点数时,必须保存成10+127=137,即10001001。
我们举例说明:
S和M取出的方式比较简单,主要E情况特殊,有三种情况:
这种情况是属于正常的情况,取出来的时候直接减去127(1023)即可;
⽐如:0.5 的⼆进制形式为0.1,由于规定正数部分必须为1,即将⼩数点右移1位,则为1.0*2^(-1),其阶码为-1+127(中间值)=126,表⽰为01111110,⽽尾数1.0去掉整数部分为0,补⻬0到23位00000000000000000000000,则其⼆进制表⽰形式为:
0 01111110 00000000000000000000000
这种情况比较特殊,我们加上了127(1023)最后E还是为全0;那只有可能E为-127(-1023),说明这是一个无限接近于0的数字;此时就不能直接减去中间数了,这个时候1-127(1-1023)即为真实值;有效数字也不在加上一,而是加上0;这样做是为了表⽰±0,以及接近于0的很⼩的数字;
这时,如果有效数字M全为0,表⽰±⽆穷⼤(正负取决于符号位s);
0 11111111 00010000000000000000000
下面我们来看一下最开始的那一道题目;