今天和大家分享一下数据在内存中是如何存储的。
一.首先我们先来看一下数据类型:
char 1个字节 字符整形
short 2个字节 短整型
int 4个字节 整形
long 4/8 长整型
longlong 8 更长的整形
float 4 单精度浮点数
double 8 双精度浮点数
size of(long)>=sizeof(int) 在32位平台上 long 是4个节、64位平台上 long是8个字节
整形家族
1.char
signed char 字符在内存中其实存的是字符对应的ASCII码值,所以也看做整形。
unsigned char
2.int
signed int
unsigned int
3.short
signed short [int]
unsigned short[int]
4. long
signed long[int]
unsigned long[int]
5. long long
signed long long[int]
unsigned long long[int]
在这里边,除了char,其他的 int、short、long、long long 其实都默认是 有符号类型,而char对应的到底是 signed char还是unsigned char取决于编译器。
浮点型家族
只要是小数,就可以用浮点型家族来表示。
float 的精度低,其存储的数据范围小,double的精度高,其存储的数据范围更大
构造类型 其实是一种自定义类型
(1)数组类型: int arr[3]
(2)结构体类型: struct
(3)枚举类型: enum
(4)联合类型: union
指针类型:
int*p
char*a
float*f
void*c
空类型:
void 表示空类型(无类型)
通常用于函数的返回类型,函数的参数、指针类型。
二.整数在内存中的存储
给大家举一个例子:
int a=20;
a 的二进制序列的原码是 00000000000000000000000000010100
在内存中存储的是a的补码,因为a是正数,所以补码和原码相同,
我们用16进制来表示二进制的这些数字,一个16进制的数字表示4个二进制数字,0000 为0
0000 为0 0000为0 0000为0 0000为0、 0000位0 0001为1 0100为4,
用十六进制表示为 0X 00 00 00 14 ,我们可以看一下它在内存中的存储情况
我们可以看到它的存储方式是 14 00 00 00,那么为什么要这样存储呢?接下来给大家讲两个概
大小端 字节序概念。 大端字节序:把一个数据的高位字节序的内容存放在低地址处,把低位字节序内容放在高地址处,就是大端字节存储。
00 00 00 14 |
低地址 高地址
小端字节序:把一个数据的高位字节序的内容存放在高地址处,把低位字节序的内容放在低地址处,就是小端字节存储。
14 00 00 00 |
低地址 高地址
有符号 char 的取值范围。-128-127, 在这里最高位的1表示正负。
00000001 1 |
00000002 2 |
.............. |
01111111 127 |
10000000 -128 |
1000000 1 -127 .................. |
11111111 -1 |
无符号char的取值范围 0-255,在这里最高位的1表示数值
00000000 0 |
00000001 1 |
.............. |
10000000 128 1*2^7 最高位表示数值不表示符号 |
10000001 129 |
10000010 130 |
............... |
11111111 255 |
大家来看一下这个题
为什么会输出这样一个结果呢?
大家请看
对于char 和 signed char 在vs编译环境下 ,char就是 signed char
10000000000000000000000000000001 -1原码
11111111111111111111111111111110 -1反码
1111111111111111111111111111 补码
发生截断
11111111
高位补充符号位补1
11111111111111111111111111111111
1000000000000000000000000000000
10000000000000000000000000000001
所以最终值为-1;
而对于unsigned char
再发生截断后,11111111
因为是无符号整数所以高位补0
00000000000000000000000011111111
又因为我们认为他是个正数(无符号数),所以补码原码相同,最终结果就为255.
大家再来看一下这几个题:
#include<stdio.h>
#include<windows.h>
int main()
{
unsigned int i=0;
for(i=9;i>=0;i--)
{
printf("%d\n",i);
Sleep(1000); //S是大写
return 0;
}
有同学可能会觉得这个题会循环打印9次 ,但其实是个死循环;
为什么是个死循环呢?因为 unsigned int 永远大于0,不会存在小于0的情况。
大家再来看一个题
我们知道strlen算的是\0之前的元素个数,\0大小为0我们看一下 a[i]是个char 类型的数据那么a[i]的取值范围是-128-127,a[i]依次是 -1,-2...........-128,127,126.........1 0,所以一共有255个数据。
三.浮点数的存储规则
任意一个二进制浮点数v可以表示为 (-1)^S*M*2^E
(-1)^S表示符号位,当S=0时,v为正数;S=1时,v为负数
M表示有效数字,大于等于1,小于2.
2^E表示指数位
给大家举个例子: 十进制的5.0 ,写成二进制是101.0,相当于1.01*2^2,按照上面的格式可以得出 S=0, M=1.01,E=2,
V=9.5f 在这里f表示数据属于float的意思, 写成二进制是 1001.1 注意小数点后分别是2^-1, 2^-2......... ( -1)^0 *1.0011*2^3,
S=0, M=1.0011 E=3,
我们来看一下它究竟是怎样放的:
对于 M和E,在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的小数部分,如保存1.01时,我们只保存 1.01,对于E情况比较复杂,,存入内存时必须再加上一个中间数,对于8位的E,这个中间数是127,对于11位的E,这个数是1023,比如2^10的E是10,所以保存成32位浮点数时,必须保存成 10+127=137,即10001001
如 v=0.5f=0.1=(-1)^0*1*2^-1 S=0, M=1.0,E=-1,
float E+127 126进行存储
double E+1023 1022进行存储
给大家举个例子,
int main()
{
float f=4.5;
4.5
100.1
1.001*2^2;
s=0; m=1.001 e=2;
e+127=129;
129 二进制表示为10000001
存储方式为 01000000100100000000000000000000
用十六进制表示为 0x40 90 00 00
又因为是小端字节序存储,我们猜测 00 00 90 40 这样来存储。
可以看到果真是这样。
然而 指数E从内存中取出还可以再分成三种情况:
E不全为0或不全为1 指数E的指针的计算减去127(或1023) ,得到真实值,再将有效数字M加上第一位的1 ,例如上边的f=4.5,其存储方式为 0 100 00001 00100000000000000000000, 将10000001减去127=2 ,尾数部分的1还原回来,所以就是(-1)*^0*1.00100000000000000000*2^(2)
E全为0时 ,这时我们可以看到e的值特别小,那么m*2(e) ,一定是个特别小的数字,这时浮点数的指数E等于1-127(或1-1023),即为真实值,这时有效数字不再加上第一位的1,而是还原为0.xxxxx的小数,这样就成为接近于0的一个很小数字 。
E全为1,此时E=255,那么e=255-127=128 那m*2^(128),的数字会特别大,这时有效数字m全为0,表示无穷大
给大家再举个例子:
#include<stdio.h>
int main()
{
int n=9;
float*p=(float*)&n;
printf("%d,n);
printf("%f,n);
*p=9.0
printf("%d",n);
printf("%f",*p);
return 0;
}
那么为什么会是这样一个结果呢? 我们看第二个结果,当打印的时候 以%f来打印时, 也就是说 我们会认为 n的二进制存储是一个float类型, 拿出来的时候也要按照 float类型来拿,
0 00000000 00000000000000000001001,我们可以看到E为全0,真实的E值则为1-127=-126那么拿出来的结果就是 0.00000000000000000001001*2^-126,会是一个趋向于0的数字。
我们再来看一下第三个printf,因为这次是按照float的形式进行存储的,9.0 二进制表示为 1001.0,
1.0010*2^3,,m为1.001,E=3, 3 二进制表示101 ,所以存储方式 0 00000101 0010000000000000000000,当以%d的形式打印时,我们会把它看做是一个整数,所以这个数很大,而当以%f形式打印时,因为它本身float类型进行存储,所以打印出来的就是float型
,