1.整数在内存中的存储
- 我们回顾一下整数的2进制表示方法:原码、反码和补码
- 正整数中:原码=反码=补码
- 负整数:三种表示方法各不相同
- 原码:直接将数值按照正负数的形式翻译成⼆进制得到的就是原码。
- 反码:将原码的符号位不变,其他位依次按位取反就可以得到反码。
- 补码:反码+1就得到补码。
- 对于整形而言:数据存放内存中其实存放的是二进制的补码
在计算机系统中,数值⼀律用补码来表示和存储。
原因在于,使⽤补码,可以将符号位和数值域统⼀处理;
同时,加法和减法也可以统⼀处理(CPU只有加法器)此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路
2.大小端字节序和字节序判断
2.1 什么是大端和小端
- 大端存储模式:数据的低位保存在内存的高地址处,高位保存在低地址处
- 小端存储模式:数据的低位保存在内存的低地址处,高位存在高地址处
2.2 为什么有大小端之分
- 计算机系统中,以字节为单位,每个地址单元对应着一个字节,一个字节为8bit位,C语言中有各种各样的类型(char-8bit,short-16bit,long-32bit)
- 另外,对于位数大于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于一个字节,必然存在着一个如何将多个字节安排的问题,因而就有了大端和小端之分。
例如: 一个16bit的short型x-内存中的地址为0x0010,x的值为0x1122-0x11高字节,0x22低字节 对于大端模式:0x11低地址,0x22高地址 对于小端模式则相反 我们常用的X86结构都是小端模式,而KEIL C51为大端模式 很多的ARM,DSP都是小段模式 有些ARM处理器可以有硬件来选择是大端模式还是小端模式2.3 与内存存储有关的练习题
判断系统是小端存储还是大端存储
#include<stdio.h>intcheck_sys(){inti=1;return(*(char*)&i);}intmain(){intret=check_sys();if(ret==1){printf("⼩端\n");}else{printf("⼤端\n");}return0;}- 对于X86系统中,采用小端存储模式
- 整形数据用4个字节表示 - 1对应着:0x0001(00 00 00 01)
- 通过访问第一个字节(取整形的地址,随后强转为char*,此时对其解引用时,按一个字节访问),就可以知道是大端还是小端
用%d打印无符号整数的情况
intmain(){chara=-1;//10000000000000000000000000000001//11111111111111111111111111111110//11111111111111111111111111111111 - 截断//11111111signedcharb=-1;//10000000000000000000000000000001//11111111111111111111111111111110//11111111111111111111111111111111 - 截断//11111111unsignedcharc=-1;//10000000000000000000000000000001//11111111111111111111111111111110//11111111111111111111111111111111 - 截断//11111111printf("a=%d,b=%d,c=%d",a,b,c);//%d - 十进制的形式打印有符号整数 - 转换成原码打印//如果数据类型本身为无符号整形,符号位默认为0//a和b都是 -1//c:默认0为符号位,则://00000000000000000000000011111111 - 255return0;}用%u打印有符号整数的情况
#include<stdio.h>intmain(){chara=-128;//10000000000000000000000010000000//11111111111111111111111101111111//11111111111111111111111110000000 - 截断//10000000 - aprintf("%u\n",a);//%u 打印无符号的10进制形式//默认把当前的数字当作无符号数,打印是高位直接补当前最高位//11111111111111111111111110000000 - 4294967168charb=128;printf("%u\n",b);//00000000000000000000000010000000//10000000//即便char的范围是-128~127,但是也可以存进去(具体的话系统会进行调整)//此时和上面同理://11111111111111111111111110000000 - 4294967168return0;}数据类型的范围
#include<stdio.h>intmain(){chara[1000];//char的范围是-128~127inti=0;//i的范围是0~4294967295for(i=0;i<1000;i++){//当a[i] = -127之后//下一个a[i] = 128//再一直减少到0 = a[255]a[i]=-1-i;}//strlen默认以'\0'结束,其本质就是0//所以到:127+128 = 255的时候,就停止计算大小printf("%d",strlen(a));return0;}3. 浮点数在内存中的存储
3.1 浮点数的存储
- S = 0 - 正数;S = 1 - 负数
- M - 有效数字:大于等于1,小于2
- E - 指数位
例如:
十进制的-5.05-101.0负数-S=1101.1=1.01×2^2->E=2总体描述:(-1)^1×1.01×2^2- IEEE 754规定
- 对于32位的浮点数(float),最⾼的1位存储符号位S,接着的8位存储指数E,剩下的23位存储有效数字M
- 对于64位的浮点数(double),最⾼的1位存储符号位S,接着的11位存储指数E,剩下的52位存储有效
数字M
- 对于32位的浮点数(float),最⾼的1位存储符号位S,接着的8位存储指数E,剩下的23位存储有效数字M
3.1.1 浮点数存储的特别规定
①1≤M<2:M可以写成1.xxxxxxxx的形式,xxxxxxxx只表示小数部分
- 计算机内部保存M的时候,默认这个数的第一位总是为1,因此不存储这位,只保留后面的xxxxxxxx 部分
- 32位的浮点数中,留给M的只有23位,第一位的1舍去后,就相当于可以保存24位有效数字
②E为一个无符号整数 - E为8位,取值范围为0~255,存入内存时真实值加上中间数127
- E位11位,取值范围为0~2047,存入内存时真实值加上中间数1023
2^10-E为10,存入到32位浮点数:E保存位:10+127=1373.1.2 浮点数取出的规定
①E不全为0或不全为1的情况:E减去127/1023,得到真实值,再在有效数字前加一位1
0.5-0.1正数部分必须为1:1.0*2^(-1)中间值E:-1+127=126-01111110尾数去掉整数部分的0,补齐到23位:00111111000000000000000000000000②E全为0:浮点数的指数E等于1-127/1023即为真实值,有效数字不加第一位的1,直接还原为0.xxxxxx的小数
00000000000000000000000000000000③E全为1:有效数字M全为0,表示无穷大
3.2 具体案例:
intn=9;float*pFloat=(float*)&n;printf("n的值为:%d\n",n);//9printf("*pFloat的值为:%f\n",*pFloat);//0.0000009还原成浮点数:00000000000000000000000000001001E为:00000000;S=0指数全为0,所以符合全01的情况: V=(-1)^0×0.00000000000000000001001×2^(-126)=1.001×2^(-146)用十进制小数表示就是0.000000