数据的存储
数据在内存中的存储探秘
一.引言
在计算机的世界里,数据的存储是一切运算和操作的基础。数据以二进制的形式存储在内存中,而不同类型的数据,其存储方式也各有特点。接下来,我们将深入探究整数、字符和浮点型数据在内存中的存储奥秘。
二、整数的存储
整数的二进制表示形式有原码、反码和补码三种。原码是直接根据数值写出的二进制序列;反码是在原码的基础上,符号位不变,其他位取反;补码则是反码加一。在二进制中,最高位为 1 表示负数,为 0 表示正数。对于正整数,原码、反码和补码是相同的 。而在内存中,整数是以补码的形式存储的,这是因为计算机内部没有减法运算,只有加法运算,补码与原码转化的算法相同,使用补码存储整数不需要额外的电路,大大简化了计算机的运算逻辑。
在内存中,数据的存储还涉及到字节序的问题,即顺序存储的方式,分为大端字节序和小端字节序。大端字节序是将低位字节序放在高地址,高位字节序放在低地址;小端字节序则是将低位字节序放在低地址,高位字节序放在高地址。我们可以通过一段简单的 C 语言代码来判断机器是大端存储还是小端存储:
#include <stdio.h>
int main() {int i = 1;char* p = (char*) & i;if (1 == *p) {printf("小端存储\n");}elseprintf("大端存储\n");return 0;}
这段代码利用指针将整数i的地址强制转换为字符指针,通过访问字符指针指向的第一个字节来判断存储方式。如果该字节的值为 1,则说明是小端存储,因为小端存储时低位字节在低地址,整数 1 的最低位字节就是 1;反之则为大端存储。
三、字符型的存储
字符型数据char在内存中的存储同样遵循一定的规则。char类型是有符号的,其取值范围是[-128, 127] 。当我们使用%u格式符打印char类型数据时,由于%u表示无符号整数,此时左边第一位不再是符号位,而是作为数值的一部分参与打印。
#include <Stdio.h>
int main() {char a[1000];int i = 0;for (i = 0; i < 1000; i++) {a[i] = -1 - i;}printf("%d ", strlen(a));return 0;}
在这个循环中,不断给字符数组a赋值,由于char类型的取值范围限制,strlen最后会取值到255
三、浮点型的存储
浮点型数据的存储与整数有很大区别,它遵循 IEEE 754 标准。任意一个二进制的浮点数都可以表示成(-1)^S * M * 2^E的形式 。
其中,(-1)^S表示符号位,当S = 0时,浮点数为正数;当S = 1时,浮点数为负数。
M表示有效数字,其范围是大于 1 小于 2。
2^E表示指数位。
对于float类型,它在内存中的存储分为三个部分:1 位符号位S、8 位指数位E和 23 位有效数字位M;double类型则是 1 位符号位S、11 位指数位E和 52 位有效数字位M 。需要注意的是,因为1 <= M < 2,即M可以写成1.xxxx的形式,其中整数部分的 1 是固定的,所以在存储时 1 不存进内存,只保留小数位。
另外,E为无符号数,对于 8 位的E,其范围是 0 - 255;对于 11 位的E,范围是 0 - 2047。但在科学计数法中E可以出现负数,所以存入内存中的E真实值必须加上一个中间数,对于 8 位E,中间数为 127;对于 11 位E,中间数为 1023。
通过下面这段 C 语言代码,我们可以直观地感受到浮点数和整数在内存存储视角上的差异:
#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("n的值为:%d\n", n);printf("pFloat的值为:%f\n", *pFloat);return 0;}
在这段代码中,首先将整数9的地址强制转换为浮点数指针,然后分别以整数和浮点数的视角打印该地址存储的值,会发现结果完全不同,这充分说明了浮点数和整数在内存中的存储方式和解读方式存在差异。当将浮点数9.0存储到该地址后,再次以整数和浮点数视角打印,结果也会相应改变,进一步验证了两种数据类型存储和解读的独特性。
四.总结
深入理解数据在内存中的存储方式,对于编写高效、准确的程序至关重要。无论是整数、字符还是浮点型数据,它们各自的存储规则都体现了计算机科学的精妙之处,为我们在编程过程中处理数据提供了坚实的理论基础。