🐍相信大家初学编程对数据在内存中是怎么存储的有很多疑问与不解,今天我专门针对这些痛点做出了一些 解决与学习 办法,帮助大家来深入理解数据在内存中是怎么存储的,因为设计到的知识点比较干货,所以一定要认真看哟,保证你可以收获满满!!!
常见的数据类型有哪些?
- c语言里我们知道常用的数据类型有:
char //字符类型(1个字节)
short //短整型(2个字节)
int //整型(4个字节)
long //长整型(4个字节)
long long //长长整型(8个字节)
float //浮点型(4个字节)
double //双精度浮点型(8个字节)
- 划分这些内置数据类型有啥意义呢?
1、我们使用上面某个内置类型的时候,在内存里开辟空间的大小不同
2、如何看待内存的视角(假如我们用float创建了一个变量,那在内存里它就会认为我们创建了一个小数,假如我们用int创建了一个变量,那在内存里它就会认为我们创建了一个整数,其它以此类推)
🐍注:通常我们创建的变量的地址都是数据类型的第一个字节的地址!!!
内存中原码、反码、补码是什么?(这个一定要理解!32位机器)
- 🐍只有负数才有原码、反码、补码的概念;正数的原码、反码、补码就是它本身代表的二进制序列!
原码:就是一个正常的整数的二进制序列。
反码:就是二进制序列的符号位(符号位就是二进制序列的第一位——0代表整数,1代表负数)不变,其它位 按位取反。
补码(事实上在内存里它存的就是这个数据的二进制补码形式):就是反码 + 1。
- 原反补的概念其实就是数据在内存里的二进制序列
- 首先来看一个整型的例子:
#include<stdio.h>
int main()
{
int a = 10;
//这是十进制的10,我们知道在计算机里它只能识别二进制 0 或 1 组成的数字
//为了方便大家看,这里就把32位的二进制序列分为1个字节1个字节的形式啦
//原码:00000000 00000000 00000000 00001010
//反码:00000000 00000000 00000000 00001010
//补码:00000000 00000000 00000000 00001010
//因为10是正数,所以它的原反补就是它本身的二进制序列!
int a = -10;
//原码:00000000 00000000 00000000 00001010
//反码:01111111 11111111 11111111 11110101
//补码:01111111 11111111 11111111 11110110
//因为-10是负数,所以以上的三个二进制序列就是它的原反补 的表现形式
//在内存里存的就是它的补码:
//补码:01111111 11111111 11111111 11110110
}
大小端字节序存储是什么?
-
我们在vs调试内存的时候是不是总是有一些疑问——比如拿10举例子:为什么我存进去的明明是——00000000 00000000 00000000 00001010 但内存里却显示0a 00 00 00(我们在内存里一般用十六进制表示形式)那就算是这样的话,那不也应该是——00 00 00 0a嘛!为什么跟我们想要的效果不一样?
-
这里面其实就涉及到了大小端字节序存储的概念:
- 大端字节序存储:把一个数据的低位字节的内容 存放在 高地址处,高位字节的内容 存放在 低地址处。
- 小端字节序存储:把一个数据的低位字节的内容 存放在 低地址处,高位字节的内容 存放在 高地址处。
- 那什么是 高低 位字节内容呢?比如一个十进制数字234——4是个位、3是十位、2是百位,那很显然最右边的就是低位字节内容,而最左边的就是高位字节内容!
- 既然都是大小端字节序存储了,那肯定都是以字节为单位的,请看下图:
- 其实这里面是有一些故事背景存在的,感兴趣的朋友可以去看一下这个小说——《格列夫游记》
-
当了解了什么是大小端字节序存储了之后,那我们就可以写一个小程序 来判断一下当前机器是大端还是小端啦:
int check_sys(void) //函数无参
{
//我们就用 1 来举例子,比较容易,因为只是判断一下是大端还是小端,别为难自己!!!
int i = 1;
//我们把 &i 强制类型转换成 char* 类型的,然后 *解引用 访问到 char*指针指向的那个对象的1个字节
return (*(char*)&i); //如果返回的是 1 那就是小端,否则直接就是大端
}
int main()
{
int ret = check_sys( );
if(ret==1)
{
printf("当前机器是小端\n");
}
else
{
printf("当前机器是大端\n");
}
return 0;
}
- 🐍相信到这里咱们应该就理解什么是大小端字节序存储了吧,如果哪里不懂的,也可以私信问我,看到了准儿 马上立刻 回答你!!!
char类型的取值范围是多少?
- 请看这个式子:char a = 128;
这个式子乍一看没啥毛病,但实际上它是有毛病的。
我们说char类型是 1 个字节的大小—那就是8个bit位,并且上面的那个表达式是有符号的。
那么请看图:
所以当char是有符号的char的时候,实际上它本身是存不进去128的,所以肯定会发生截断
截断:数据字节大的内容,放到数据字节小的内容里,二进制位根据数据的大小进行截断
注意:
原码 - 按位取反得反码 - 反码+1得补码——我们简称这个过程叫 按位取反+1
但是从补码到原码也可以用 按位取反+1来从补码得到原码,不过这里的按位取反就不是得到反码了,谁知道它得到的是什么呢?反正最后可以得到原码就对了!!!
请看代码:
int main()
{
char a = 128;
// 128是一个十进制整数,它要放到char类型的空间里的话,必然会发生截断!
//128的补码:00000000 00000000 00000000 10000000
//这里128的二进制是32个bit位,但char类型的大小只能占有8个bit位,所以会发生截断!
//截断后:10000000 - -128
//然后我们又要用%d打印出来,那就会发生整型提升。
//整型提升规则:如果是无符号数那高位直接补0;如果是有符号数,那它就会把高位的数看成符号位
//然后直接按符号位补齐32个bit位
//上边的char a默认是有符号的,所以它会把10000000中的1看成符号位,然后直接补齐。
//补齐后:
//11111111 11111111 11111111 10000000 - 补码
//10000000 00000000 00000000 01111111 - 不是反码
//10000000 00000000 00000000 10000000 - 原码
printf("%d\n",a); // 所以打印出来的就是-128
}