目录
一.整型在内存中的存储
整数的2进制表示方法有三种,即原码、反码和补码
有符号的整数,三种表示方法均有符号位和数值位两部分,符号位都是用0表示正,用1表示负,最高位的一位是被当做符号位,剩余的都是数值位
正整数的原、反、补码都相同
负整数的三种表示方法各不相同
原码:直接将数据按照正负数的形式翻译成二进制得到的就是原码
反码:将原码的符号位不变,其他位一次按位取反可以得到反码
补码: 反码+1就得到补码
计算使用的是内存中的二进制,计算使用的就是补码
1.大小端字节序和字节序判断
int main()
{
int a = 0x11223344;
return 0;
}
我们会发现这其实是倒着存放的
2.1 大小端字节序
大端字节序存储:将一个数据的低位字节,内容存放在内存的高地址处,高位字节的内容存放在低地址处
什么意思呢
44其实就对应了个位的3,低位的字节放在了高地址的一侧就叫做大段字节序存储
小段字节序存储:将一个数据的低位字节,内容存放在内存的低地址处,高位字节的内容存放在高地址处
vs采用的是小段存储
2.2为什么有大小端
这是因为在计算机系统中,我们是以字节为单位的,每个地址单元都对应这一个字节,一个字节为8bit位,但是在c语言中除了8bit的char以外,还有16bit的short类型等等,另外,对于位数大于8位的处理器,由于寄存器宽度大于一个字节,那么必然存在这一个如何将多个字节安排的问题,因此就导致了大端存储模式和小段存储模式。
我们常用的x86结构是小段模式,而KEIL C51则为大端模式,很多的ARM,DSP都为小段模式。有些ARM处理器还可以有硬件来选择是大端模式还是小端模式
int main()
{
int a = 1;
if (*(char*)&a == 1)
printf("小端\n");
else
printf("大端\n");
}
这样就能检测处当前机器是大端还是小段
因为0x 01 00 00 00和0x 00 00 00 01我们只要判断首位是不是1就可以了
int main()
{//-1
//100000000000000000000000000000001
//111111111111111111111111111111110
//111111111111111111111111111111111
//拿出来8个bit位进行存储发现打印的是整型进行整型提升
//11111111整型提升有符号位往前补1
//111111111111111111111111111111111就跟原来一样了
char a = -1;
signed char b = -1;
//而无符号位整型提升往前面补0
//00000000000000000000000011111111
//这就是原码了,读出来的大小为255
unsigned char c = -1;
printf("a=%d,b=%d,c=%d", a, b, c);
return 0;
}
int main()
{
char a = 128;
char b = -128;
printf("a=%u\nb=%u\n", a, b);
return 0;
}
//首先我们来看b
//10000000000000000000000010000000
//11111111111111111111111101111111
//11111111111111111111111110000000
//整型提升由于是char类型的往前补1
//11111111111111111111111110000000
//打印无符号整型就变为了一个很大的数字
//其次我们来看a
//00000000000000000000000010000000
//正数没有原反补
//10000000
//那就和上面变得一样了
实际上char类型根本存不下那么大的数字char类型的取值范围是-128-127
那里打错了是127
10000000是没法计算的,我们默认1后面跟7个0是为-128
实际上我们+1+1+1加到127再加1就只能到-128到不了128所以说a=128实际上存的是-128
其实类型的作用不是很大,关键的是你如何看待这块空间的数据
int main()
{
signed int a = -10;
printf("a=%d\n", a);
printf("a=%u\n", a);
unsigned int b = -10;
printf("b=%d\n", b);
printf("b=%u\n", b);
return 0;
}
我们会发现其最终的结果都是一样的,其实都是在内存中开辟一块4个字节的空间去存储,关键的是你怎么去看他
那么有什么用呢
signed int --有符号 我们匹配%d
unsigned int--无符号我们匹配%u
就是为了书写规范
int main()
{
char a[1000];
int i = 0;
for (i = 0; i < 1000; i++)
{
a[i] = -1 - i;
}
printf("%d", strlen(a));
}
以为的是上面这种类型,因为strlen统计的是'\0'之前的字符串长度,所以有人以为这是错误的
其实char类型的大小只有-128-127 ,当变为-128的时候接下来再-1就只能变成127所以这就是255的由来了
unsigned char i = 0;
int main()
{
for (i = 0; i <= 255; i++)
{
printf("hello world");
}
return 0;
}
实际上这里是无限循环,因为unsigned char的范围是0-255,所以i<=255判断恒为真,所以持续打印
int main()
{
unsigned int i;
for (i = 9; i >= 0; i++)
{
printf("%u\n", i);
}
}
和这个代码一样无符号整型始终大于0,恒为真
int main()
{
int a[4] = { 1,2,3,4 };
int* ptr1 = (int*)(&a + 1);
int* ptr2 = (int*)((int)a + 1);
printf("%x,%x", ptr1[-1], *ptr2);
}
取地址取出的是数组的地址,数组的地址+1跳过的是整个数组,而ptr1[-1]可以看作*(ptr1-1)取出的其实就是4的地址
(int)a就是把首元素的地址转换为整型那么就真的是把第一个字节转换成整型假设是0x10,整型+1就是0x11,向后挑了一个字节,然后转换为整型指针又指向了00的那个位置,然后ptr2是整型指针向后访问四个字节就是00 00 00 02,然后我们x86环境底下的小段字节序存储,就是0x0 2 00 00 00前面0x0可以被处理器省略
二.浮点数在内存中的存储
常见的浮点数:3.14159,1E10等,浮点数家族包括:float,double,long double类型。
浮点数表示的范围:float.h中定义
int main()
{
int n = 9;
float* pFloat = (float*)&n;
printf("n的值为:%d\n", n);
printf("*pFloat的值为;%f\n", *pFloat);
*pFloat = 9.0;
printf("n的值为:%d\n", n);
printf("*pFloat的值为;%f\n", *pFloat);
}
整数和浮点数在内存中的存储方式是有区别的
3.2浮点数的存储
根据国际标准IEEE(电气和电子工程协会)754,任意一个二进制浮点数v可以表示成下面的形式:
V=(-1)的s次方*M*2的E次方
(-1)的S次方表示符号位,当S=0,V为整数;当S=1,V为负数
M表示有效数字,M是大于等于1,小于2的
2的E次方表示指数位
浮点数的存储,存储的就是S,M,E相关的值
3.2.1浮点数存的过程
IEEE754对有效数字M和指数E,还有一些特别规定
前面说过,1<=M<2也就是说,M可以写成1.xxxxx的西南公司,其中xxxxx表示小数部分。
IEEE754规定,在计算机内部保存M的时候,默认这个数的第一位总是1,因此可以被舍去,只保存后面的xxxx部分,比如保存1.01的时候只保存0,等到读取的时候,再把第一位的1加上去。这样做的目的,是节省1位有效数字。以32位浮点数为例,留给M只有23位,将第一位的1舍去后,等于可以保存24位有效数字
至于E情况就比较复杂
首先E为一个无符号整数
这意味这,如果E为8位,它的取值范围为0-255;如果E为11位,它的取值范围为0-2047.但是我们知道,科学计数法中的E是可以出现负数的,所以IEEE754规定,存入内存时E的真实值必须再加上一个中间数,对于8位的E,这个中间数时127,对于11位的E这个中间数时1023.比如2的10次方的E时10.所以保存成32位浮点数时,必须保存成10+127=137,即10001001
3.2.2浮点数取的过程
指数E从内存中取出还可以分成三种情况
E不全为0或不全为1(常规情况)
这时,浮点数就采用下面的规则表示,即指数E的计算值减去127或者是1023,得到真是值,再将有效数字M前加上第一位的1
E为全0
这时,浮点数的指数等于1-127或者1-1023即为真实值,有效数字M不在加上第一位的1,而是还原为0.xxxxx的小数,这样做是为了表示±0,以及接近0的很小的数字
E全为1
这时1如果有效数字M全为0,表示±无穷大
int main()
{
float f = 5.5f;
//S=0
//E=2
//M=1.011
//0100 0000 1011 0000 0000 0000
//0X 40 b0 00 00
}
int main()
{
int n = 9;
//00000000 00000000 00000000 00001001
float* pfloat = (float*)&n;
printf("n的值为:%d\n", n);//9
//0 0000000 00000000 00000000 00001001
//s=0
//M=0-127=-127但是按照规则来实际上是1-127=-126
//那么实际上就是0.0000001001*2的-126次方无限接近于0
printf("*pfloat的值为;%f\n", *pfloat);
*pfloat = 9.0;
//1001.0
//1.001*2的3次方
// S=0
//M=1.001
//E=3
//0100000100010000000000000000
printf("n的值为:%d\n", n);//这里再以整数的形式打印出来就会变得非常大
printf("*pfloat的值为;%f\n", *pfloat);//9.000000
ok数据在内存中的存储就到这里了,写的不足的地方欢迎大家指正。