目录
前言
数据是计算机中极为重要的元素,所以数据的存储是我们成为技术大牛所必备的。
今天,我们一起来初步了解数据在内存里的存储。
————————————————————————————————————
一、数据类型详细介绍。
数据类型介绍如下:
我们对数据进行分类的原因是:我们可以合理地为数据开辟内存空间。
1.1、数据的几大分类
1、整型家族
注:char类型在不断的编译器里可能默认是signed 的,也可能是unsigned的。
2、浮点数家族
float 和 double
3、结构家族
4、指针类型家族
5、空类型
void test(void)
{
void* p;
}
我们知道我们给a分配了四个字节的空间,但是如何存储?
为此,我们需要进一步了解 原码、反码、补码 的概念。
二、整型在内存中的存储。
比如:
2.1、 原码、反码、补码
计算机里整数有三种表示方法。即原码。反码,补码。(为二进制)
// 10000000000000000000000000000001 -->1的原码
第一个1是符号位,剩下的是数值位。
其中正负数的原反补规则也有差别。
负整数:
原码直接将二进制按照正负数的形式翻译成二进制就可以。反码将原码的符号位不变,其他位依次按位取反就可以得到了。补码反码+1 就得到补码。
如:
// 10000000000000000000000000000001 --> -1的原码
// 11111111111111111111111111111110 --> -1的反码
// 11111111111111111111111111111111 --> -1的补码
正整数:
正整数的原反补都一样。
对于整型而言,数据是以补码的形式存于数据中的。
why?因为
在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统一处理;同时,加法和减法也可以统一处理( CPU 只有加法器 )此外,补码与原码相互转换,其运算过程 是相同的,不需要额外的硬件电路
我们可以观察一下:
这是a变量在内存中的情况。
这是b在内存中的情况。
我们可以看出来存的确实是补码。但,我们也发现似乎顺序不太对。
这就涉及下一个知识点。
2.2、大小端介绍
啥是大小端?
大端(存储)模式,是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中。小端(存储)模式,是指数据的低位保存在内存的低地址中,而数据的高位 , ,保存在内存的高地址中。
比如我使用的vs2019就是小端。
至于为啥有大小端,还是看看官方的解释吧
这是因为在计算机系统中,我们是以字节为单位的,
每个地址单元都对应着一个字节,一个字节为8bit。
但是在C语言中除了8bit的char之外,还有16bit的short型,
32bit的long型(要看具体的编译器),另外,对于位数大于8位的处理器,
例如16位或者32位的处理器,由于寄存器宽度大于一个字节,
那么必然存在着一个如果将多个字节安排的问题。
因此就导致了大端存储模式和小端存储模式。
例如一个16bit的short型x,在内存中的地址为0x0010,x的值为0x1122,
那么0x11为高字节,0x22为低字节。
对于大端模式,就将0x11放在低地址中,
即0x0010中,0x22放在高地址中,即0x0011中。小端模式,刚好相反。
我们常用的X86结构是小端模式,而KEIL C51则为大端模式。
很多的ARM,DSP都为小端模式。有些ARM处理器还可以由硬件来选择是大端模式还是小端模式。
我们也可以设计个函数来进行判断大小端。
int check_sys()
{
int a = 1;
return *(char*)&a;
}
2.3、练习加深理解
int main()
{
char a = -1;
signed char b = -1;
unsigned char c = -1;
printf("a=%d,b=%d,c=%d", a, b, c);//-1,-1,255
return 0;
}
结果是-1,-1,255。理由:
存char是只有8个比特位,打印时会发生整型提升。
在vs中char默认是signed char。
无符号位整型提升补0。所以-1的在无符号里补码会变 000000000000000000000011111111,所以是255.
int main()
{
char a = -128;
printf("%u\n", a);//4294967168 --> 11111111111111111111111110000000
//整型提升后,因为%u所以直接补码是原码给打印出来
char b = 128;
printf("%u\n", b);//打印结果和原理与负数一致
return 0;
}
这题也是同理。
int main()
{
int i = -20;
unsigned int j = 10;
printf("%d\n", i + j);//-10,补码相加可得
return 0;
}
这里就是二进制计算了。
#include <windows.h>
int main()
{
unsigned int i;
for(i = 0; i >= 0; i--)
{
printf("%u\n", i);
Sleep(1000);
}
//4294967295开始打印,然后减一再打印进入死循环
return 0;
}
输出结果如上,之后就会进入死循环。
原因:i=0,减一变-1了,但是是无符号的i,所以会直接补码是原码,然后就是4294967295开始。
接下来是很难理解的一题。
#include <string.h>
int main()
{
char a[1000];
int i;
for (i = 0; i < 1000; i++)
{
a[i] = -1 - i;
}
printf("%d\n", strlen(a));//127+128
return 0;
}
结果是255.
理由:
利用这样一个图,可以清晰理解。,当再次遇到\0也就是0的世界上投入了strlen函数停止,所以是127+128=255;
#include <stdio.h>
unsigned char i = 0;
int main()
{
for (i = 0; i <= 255; i++)
{
printf("hello world\n");//死循环,因为i的值是0-127
}
return 0;
}
相对而言这题就比较好理解。
三、浮点数在内存中的存储。
1.一个典型例子
int main()
{
int n = 9;
float* pFloat = (float*)&n;
printf("n的值为:%d\n", n);//9
printf("*pFloat的值为:%f\n", *pFloat);//0.00000
*pFloat = 9.0;
printf("num的值为:%d\n", n);//1091567616
printf("*pFloat的值为:%f\n", *pFloat);//9.0000...
return 0;
}
结果可能会让人惊讶:
第二个和第四个多少给我们带来了困惑。想要理解的话我们需要先知道浮点数存储规则。
2.浮点数存储规则
有国际规定(IEEE754)有:二进制的表示方式
(-1)^S * M * 2^E(-1)^s 表示符号位,当 s=0 , V 为正数;当 s=1 , V 为负数。M 表示有效数字,大于等于 1 ,小于 2 。2^E 表示指数位。