嘿嘿,家人们,今天咱们来深度剖析数据类型在内存中的存储,好啦,废话多不讲,开干!
在前面呢,博主已经介绍了基本的数据类型:
char //字符数据类型 ---->占据1个字节 short //短整型 ---->占据2个字节 int //整形 ---->占据4个字节 long //长整型 ---->占据4个字节 long long //更长的整形 ---->占据8个字节 float //单精度浮点数 ---->占据4个字节 double //双精度浮点数 ---->占据8个字节
那么这些基本的数据类型,这些类型的意义在哪呢?
char unsigned char signed char PS:字符型的数据在内存中是以ascii码值的方式存储的,因此属于整型家族. short unsigned short int(无符号短整型) signed short int(有符号短整型) int unsigned int(无符号短整型) signed int(有符号短整型) long unsigned lont int(无符号长整型) signed int(有符号长整型)
float 浮点型 double 双精度浮点型
数组类型 结构体类型 struct 枚举类型 enum 联合类型 union
void 表示空类型(无类型). 通常应用于函数的返回类型、函数的参数、指针类型.
在操作符的时候,博主有讲解到原码,反码,补码的相关知识,这里带着uu们简单回顾下.
对于整形来说: 数据存放在内存中其实存放的是补码.这是为什么呢
(1):在计算机系统中,数值⼀律⽤补码来表⽰和存储。原因在于: 使⽤补码,可以将符号位和数值域统⼀处理. (2): 同时,加法和减法也可以统⼀处理( CPU只有加法器 )此外,补码与原码相互转换,其运算过程是 相同的,不需要额外的硬件电路. 这里我们举个几个简单的例子

当我们了解了整数在内存中的存储方式后,接下来我们来通过调试看一个小细节.

通过调试,我们能够清晰地看到,在a中的 0x12345678 这个数字是按照字节为单位,倒着存储的。这是为什么呢?这里就要涉及到大小端存储啦
超过⼀个字节的数据在内存中存储的时候,就有存储顺序的问题,按照不同的存储顺序,将其分为 ⼤端字节序存储和⼩端字节序存储 .

当了解了大小端的概念后,那么我们该如何判断一个机器是大端存储方式还是小端存储方式.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int check_sys()
{
int value = 1;
return (*(char*)&value & 1);
}
int main()
{
int result = check_sys();
if(result == 0)
{
printf("大端存储\n");
}
else
{
printf("小端存储\n");
}
}


了解了整数在内存中的存储后,接下来我们来看几个小练习
#define _CRT_SECURE_NO_WARNINGS
#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;
}

#include <stdio.h>
int main()
{
char a = -128;
printf("%u\n",a);
return 0;
}

#include <stdio.h>
int main()
{
char a = 128;
printf("%u\n",a);
return 0;
}

#include <stdio.h>
#include <string.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>
unsigned char i = 0;
int main()
{
for(i = 0;i<=255;i++)
{
printf("hello world\n");
}
return 0;
}
上述代码呢,很明显,会发生死循环,那么是为什么呢?

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
unsigned int i;
for (i = 9; i >= 0; i--)
{
printf("%u\n", i);
}
return 0;
}
通过观察上面的结果,我们可以看到,此代码也会发生死循环,那么是为什么呢,有的uu会很奇怪,当i ==0时,此时再对其进行--,不应该是到 -1了吗,那么这是为啥呢?


常见的浮点数:3.14159、1E10等,浮点数家族包括:float、double、long double 类型.那么浮点数在内存中是如何存储的呢,我们首先来看下面这段代码.
#include <stdio.h>
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;
}
num和*pFloat明明在内存中明明是同一个数,为什么浮点数和整数的解读结果会差别这么大,这是为什么呢?这里就涉及到了浮点数在内存中的存储方式了.
根据国际标准IEE754标准,任何一个二进制浮点数V可以表示成下面的形式.
了解了浮点数的存储规则后,我们来看几个例子.


对于32位的浮点数,最高的1位存储符号位S,接着的8位存储指数E,剩下的23位存储有效数字M.

对于 64位 的浮点数, 最⾼的1位存储符号位S ,接着的 11位存储指数E ,剩下的 52位存储有效数字M .

IEE754对有效数字M和指数M,有一些特别的规定.
在上面有提到过,1 <= M < 2,那么也就是说,M可以写成1.xxxxxxx的形式,其中xxxxxxx表示小数部分. IEE754规定,在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的xxxxxx部分.比如保存1.01的时候,只保存01,等到要对其进行读取的时候,再把第一位的加上去.这样子做的目的是为了节省一位有效数字.以64位浮点数为例,留给M的只有52位,将第一位的1加进来以后,等于可以保存53位有效数字.
至于指数E,情况就有些复杂.
首先,E为一个无符号整数(unsigned int)
这就意味着,若E为8位,那么它的取值范围为0~255;如果E为11位,它的取值范围为0~2047.但是,我们知道,科学计数法中的E是可以出现负数的,所以IEE754规定,存入内存E的真实值必须再加上一个中间数,对于8位的E,这个中间数是127;对于11位的E,这个中间数是1023.例如,2^10的E是10,所以保存为32位浮点数时,必须保存成10 + 127 = 137.即10001001.
指数E从内存中取出还可以再分成三种情况
这时,浮点数就采用下面的规则表示,即指数E的计算值减去127(32位浮点数)或1023(64位浮点数),得到真实值,再将有效数字M前加上第一位的1. 比如:0.5的二进制形式为0.1,由于规定正数部分必须为1,即将小数点右移1位,则为1.0*2^(-1),其阶码为-1 + 127 = 126,表示为01111110,而尾数1.0去掉整数部分为0,补齐0到23位,那么二级制表示形式则为 0 01111110 00000000000000000000000.
这时,浮点数的指数E等于1-127(或者1-1023)即为真实值,有效数字M不再加上第⼀位的1,而是还原为0.xxxxxx的⼩数。这样做是为了表示±0,以及接近于0的很小的数字。
这个时候,如果有效数字M全为0,表示±无穷大.
了解了浮点数的存储过程后,我们再回到最初的代码
#include <stdio.h>
int main()
{
int n = 9;
float* pFloat = (float*)&n;
printf("n的值为:%d\n", n);
9
/*
补码:000000000000000000000000000001001
从浮点数存储的角度出发
0(S) 00000000(E) 000000000000000000001001(M)
由于E为全0,那么此时E的真实值是1 - 127(32位) = -126
M = 0.000000000000000000001001
S = 0
因此V = (-1)^0 * 0.000000000000000000001001 * 2^-126;*/
printf("*pFloat的值为:%f\n", *pFloat);//无限接近于0
*pFloat = 9.0;
/*二进制序列:1001.0
V = (-1)^0 * 1.001 * 2^3;
S = 0, M = 1.001 , E = 3;
将其还原为二进制序列时,
首先E + 127(32位) = 130------->10000010
M去掉整数位后为001,然后后面补0,
因此二进制序列是
0(S) 10000010 (3 + 127---->E)001 0000 0000 0000 0000 0000*/
printf("num的值为:%d\n", n);
printf("*pFloat的值为:%f\n", *pFloat);
return 0;
}
9 补码:000000000000000000000000000001001 从浮点数存储的角度出发 0(S) 00000000(E) 000000000000000000001001(M) 由于E为全0,那么此时E的真实值是1 - 127(32位) = -126 M = 0.000000000000000000001001 S = 0 因此V = (-1)^0 * 0.000000000000000000001001 * 2^-126-------->无线接近于0 9.0 二进制序列:1001.0 V = (-1)^0 * 1.001 * 2^3; S = 0, M = 1.001 , E = 3; 将其还原为二进制序列时,
因此二进制序列是 0(S) 10000010 (3 + 127---->E)001 0000 0000 0000 0000 0000


好啦,uu们,数据类型在内存中的存储这部分滴详细内容博主就讲到这里啦,如果uu们觉得博主讲的不错的话,请动动你们滴小手给博主点点赞,你们滴鼓励将成为博主源源不断滴动力,同时也欢迎大家来指正博主滴错误~