目录
1.整数在内存中的存储
负数的反码需要将原码按位取反(原码为1则取0,为0则取1,第一位为符号位不动),得到反码后给最后一位加上1,则得到了补码。原码:直接将数值按照正负数的形式翻译成⼆进制得到的就是原码。反码:将原码的符号位不变,其他位依次按位取反就可以得到反码。补码:反码+1就得到补码。
整形在内存中的存储都是补码
原因:在计算机系统中,数值⼀律⽤补码来表⽰和存储。原因在于,使⽤补码,可以将符号位和数值域统⼀处理;同时,加法和减法也可以统⼀处理(CPU只有加法器)此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。
int a = 3;
//a是整形是4个字节,32个bit位
//a的二进制序列是 00000000 00000000 00000000 00000011;
//因为其为正数则原码等于反码等于补码,则a = 3在内存中存放的序列(补码)为
// 00000000 00000000 00000000 00000011
int b = -5
//b是整形是4个字节,32个bit位
//b是负数则第一位用来表示正负(0为正,1为负数)
//b的二进制序列是 10000000 00000000 00000000 00000101;
//因为b为负数则
//原码:10000000 00000000 00000000 00000101
//反码:11111111 11111111 11111111 11111010
//补码:11111111 11111111 11111111 11111011
//11111111 11111111 11111111 11111011就是b = -5在内存存放的序列(补码)
2. ⼤⼩端字节序和字节序判断
当我们给a存入一个16进制(0x代表16进制)的值时我们在内存看到一个奇怪的现象
#include <stdio.h>
int main()
{
int a = 0x11223344;
return 0;
}
![](https://i-blog.csdnimg.cn/direct/634abe520f364bc8b18bb9132ee50309.png)
我们发现a的地址上存放的和我们写入的顺序是相反的,这是为什么呢?
2.1 什么是⼤⼩端?
其实超过⼀个字节的数据在内存中存储的时候,就有存储顺序的问题,按照不同的存储顺序,我们分为⼤端字节序存储和⼩端字节序存储,下⾯是具体的概念:⼤端(存储)模式:是指数据的低位字节内容保存在内存的⾼地址处,⽽数据的⾼位字节内容,保存 在内存的低地址处。⼩端(存储)模式:是指数据的低位字节内容保存在内存的低地址处,⽽数据的⾼位字节内容,保存 在内存的⾼地址处。
(数据在存储的时候可以是以任何顺序,例如44在中间,33在第一;或者11在中间44放最后等等。为了统一,规定了大小端。)
还是以刚才的例子
int a = 0x11223344;
这是我们要存入的a的数据,从左到右位置逐渐降低,则44为低位字节处,
在内存中地址是从左到右依次升高,所以当我们看到44在最左边时,则可以得出我们的电脑是小端存储,我们可以进行试验
下面让x等于5看他在内存中的存储
int main()
{
int a = 0x11223344;
int x = 5;
//x = 5的二进制序列为:00000000 00000000 00000000 00000101
//5为正数则原码为补码,则x在内存中存放的序列为00000000 00000000 00000000 00000101
//当我们想看到16进制的存储方式时,16进制的范围是 0~15,对应的2进制是0000~1111,我们可以说4个2进制可以转换为一个16进制
//则 0000 0000 0000 0000 0000 0000 0000 0101
//相当于 0 0 0 0 0 0 0 5
//16进制为0x 00 00 00 05
//如果是小端存储则我们看到的内存就是, 小字节位存在小地址上 依次为05 00 00 00
return 0;
}
我们先按住F10然后在调试窗口上看内存
再在内存上 &x 看到x的内存
正如我们所预测的我的计算机是小端存储
2.2 为什么有⼤⼩端?(补充)
2.3用一些例子来看存储
练习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;
}
我们知道signed是指有符号数,unsigned是指无符号数
这里指的有无符号数实质上是,在内存中存储的数据二进制的第一位是否作为符号位
(1为负数,0为正数)。
补充:
char 是整形家族的一员(字符在内存中的储存是以ASCII码值的形式储存的,所以被归为整形)
%d是以十进制的形式,打印有符号的整
%u是以十进制的形式,打印无符号的整数
当我们直接说char的时候是指signed char 这时他的二进制序列的第一位就是符号位
unsigned char 则他的第一位就是普通的bit位不是符号位
当我们把一个整形(int 4个字节)放进一个char类型的空间时,
-1的
原码:100000000 00000000 00000000 00000001
反码:11111111 11111111 1111111 11111110
补码:11111111 11111111 1111111 11111111
而char只有一个字节8个bit位,所以我们传的只有后面8个bit位 11111111 - a(存在a中的值)
a 的补码是 11111111,要求其原码,先求反再加一
补码:11111111
反码:1000000
原码:1000001
我们求得原码也知道a = -1,a和b是相同的
c是unsigned类型 第一位不是符号位 ,我们此时求原码
补码:11111111,因为c是unsigned所以没有负数,补码即是原码
原码:11111111
c= 2^7+2^6+2^5+2^4+2^3+2^2+2^1+1 = 255
练习2.
#include <stdio.h>
int main()
{
char a = -128;
printf("%u\n",a);
return 0;
}
-128的二进制为:100000000 00000000 00000000 10000000
原码:100000000 00000000 00000000 10000000
反码:11111111 11111111 11111111 01111111
补码:11111111 11111111 11111111 10000000
我们存入的补码就是后8位bit10000000
char a = -128的补码就是10000000
当我们要printf("%u", a);
因为 %u是要打印无符号整形,所以我们要进行整形提升(整形提升补符号位)
则a的补码就变成了, 11111111 11111111 11111111 10000000
且为无符号数原码,反码,补码相同 那么我们打印的数大于2^31
练习3 :
#include <stdio.h>
int main()
{
char a[1000];
int i;
for (i = 0; i < 1000; i++)
{
a[i] = -1 - i;
}
printf("%d", strlen(a));
return 0;
}
解析:
#include <stdio.h>
int main()
{
char a[1000];
//-1,-2,-3,-4···-128,127,126···3,2,1,0
///128+127=255
int i;
for (i = 0; i < 1000; i++)
{
a[i] = -1 - i;
}
printf("%d", strlen(a));
//strlen打印的是字符0之前的数字个数
//所以,a[1000],最大是255
return 0;
}
signed char 在内存中类似于一种循环的方式,unsigned char第一位不作为符号位可以正常读
由此我们可以得出上题的答案是255 。
3. 浮点数在内存中的存储
#include <stdio.h>
int main()
{
int n = 9;
float *pFloat = (float *)&n;
printf("n的值为:%d\n",n);//可以轻松知道打印出来是9
printf("*pFloat的值为:%f\n",*pFloat);//??
*pFloat = 9.0;
printf("num的值为:%d\n",n);//???
printf("*pFloat的值为:%f\n",*pFloat);//易知打印出来应该是9.0
return 0;
}
那么*pFloat的值是多少?
那么 num的值是多少?
3.1 浮点数的存储
根据国际标准IEEE(电⽓和电⼦⼯程协会) 754,任意⼀个⼆进制浮点数V可以表⽰成下⾯的形式:
V = (−1) ^ S* M * 2^ E1.(−1) S 表⽰符号位,当S=0,V为正数;当S=1,V为负数2.M 表⽰有效数字,M是⼤于等于1,⼩于2的3. 2 ^ E 表⽰指数位
![](https://i-blog.csdnimg.cn/direct/21299995b9ba44fdbd8d7e4b99bb2f02.png)
![](https://i-blog.csdnimg.cn/direct/07fd57fd6f864224a59fda87f8204feb.png)
![](https://i-blog.csdnimg.cn/direct/52c66c8535db461fba5ff7a7c852e628.png)
3.1.1 浮点数存的过程
1.前面我们提到过1<=M<2所以M总是写成,1.xxxxxxx的形式,因此我们在存放M的时候可以去掉1,直接存储.xxxxxxxxx的数据到剩下的23bit(32位)中来提高我们的精度
EEE 754规定,在计算机内部保存M时,默认这个数的第⼀位总是1,因此可以被舍去,只保存后⾯的xxxxxx部分。比如保存1.01的时候,只保存01,等到读取的时候,再把第⼀位的1加上去。这样做的目 的,是节省1位有效数字。以32位浮点数为例,留给M只有23位,将第⼀位的1舍去以后,等于可以保 存24位有效数字。
这意味着,如果E为8位,它的取值范围为0~255(32中E是8位,2^8);如果E为11位,它的取值范围为0~2047(64中E是11位,2^11)。但是我 们知道,科学计数法中的E是可以出现负数的,所以IEEE 754规定,存⼊内存时E的真实值必须再加上 ⼀个中间数,对于8位的E,这个中间数是127;对于11位的E,这个中间数是1023。⽐如,2^10的E是10,所以保存成32位浮点数时,必须保存成10+127=137,即10001001。例如:有0.5的存在他的二进制为0.1,E的值是负数,
3.1.2 浮点数取的过程
3.2 题目解析
#include <stdio.h>
int main()
{
int n = 9;
float *pFloat = (float *)&n;
printf("n的值为:%d\n",n);//可以轻松知道打印出来是9
printf("*pFloat的值为:%f\n",*pFloat);//??
*pFloat = 9.0;
printf("num的值为:%d\n",n);//???
printf("*pFloat的值为:%f\n",*pFloat);//易知打印出来应该是9.0
return 0;
}
解析如下
#include<stdio.h>
int main()
{
int n = 9;
//我们把9存入n的时候,
//00000000 00000000 00000000 00001001这是9的原码也是反码补码
float* pFloat = (float*)&n;
//当我们把n的整形二进制序列,改为浮点型时
//在浮点型看来
//00000000 00000000 00000000 00001001
//0 00000000 00000000000000000001001这是浮点型位置看来的数据存储
//S E M
//S = 0;E = 00000000; M = 00000000000000000001001我们前面提到过E全为0
//这时,浮点数的指数E等于1 - 127(或者1 - 1023)即为真实值,有效数字M不再加上第⼀位的1,
//⽽是还原为0.xxxxxx的⼩数。这样做是为了表⽰±0,以及接近于0的很⼩的数字。
//E = -126;M = 0.00000000000000000001001
//V = (-1)^0 * 00000000000000000001001 * 2^-126
//这是一个很小的数,无线接近与0,那么我们打印出来也应该是0
printf("n的值为:%d\n", n);
//我们初始化n,再打印n,没有问题
printf("*pFloat的值为:%f\n", *pFloat);
*pFloat = 9.0;
// 浮点数9.0
//十进制9.0
//二进制1001.0
// 二进制1001.0 = 1.001*2^3
// S = 0
// E = 3
// M = 1.001
// 在内存中存放的时候 E = 3 + 127 = 130
// 其二进制为 10000010
// 在内存中存放的时候 M = 0010000000000000000000( 后面进行补位0)
// 0 10000010 0010000000000000000000
printf("num的值为:%d\n", n);
// 在整形位置看来这个0 10000010 001 0000 0000 0000 0000 0000就是他的补码也是原码(正数)
//所以n 打印出来是2^30+2^24+2^20等于1,000,000,000多
printf("*pFloat的值为:%f\n", *pFloat);//存9.0得9.0
return 0;
}
4.最后
相信大家看完上面的代码可以了解数据在内存中是如何存储的,欢迎指正。