C语言深度剖析:整数与浮点数在内存中的存储
前言我们在C语言中常用的数据类型主要分为两大类整型和浮点型。很多初学者只会简单赋值、打印却不知道数据在内存中到底是怎么存放的。整数和浮点数的存储规则完全不一样这也是面试、考试高频考点。本文用最简单直白的语言彻底讲明白二者存储原理。一、整数在内存中的存储1. 整数存储规则补码存储计算机中整数统一以二进制补码形式存放。原码直观写出二进制最高位为符号位0正数1负数。反码正数不变负数符号位不变其余按位取反。补码正数不变负数反码1。为什么要用补码补码可以让加减法统一成加法运算并且消除 0、-0 的二义性硬件计算效率更高。2. 大小端字节序当数据占用多个字节时就会出现字节排序问题也就是大小端。小端模式主流PC、手机低字节存低地址高字节存高地址。大端模式网络、单片机高字节存低地址低字节存高地址。重点结论变量取地址 a 永远拿到变量的最低起始地址大小端只改变字节排布不改变首地址。3. 大小端笔试题举例unsigned int a 0x1234; unsigned char b *(unsigned char *)a;小端低地址放低位字节 →b 0x34大端低地址放高位字节 →b 0x00二、浮点数在内存中的存储1. 浮点存储标准IEEE754C语言中 float、double 全部遵循IEEE754 浮点数规则和整数存储完全不同。任意浮点数公式$$V (-1)^S \times M \times 2^E$$S符号位0为正数1为负数E指数位存放偏移后的指数M尾数位保存小数部分2. float 与 double 内存分布1float4字节32位S1 bitE8 bit偏移量 127M23 bit2double8字节64位S1 bitE11 bit偏移量 1023M52 bit3. 隐藏整数1二进制科学计数法永远写成1.xxxxx的形式。所以整数部分的1 不需要存自动省略节省1bit空间读取时自动补回。4. 特殊规则考试常考正常情况E不全0、不全1按公式计算。E全0不再补1当作0.xxxx用来表示接近0的极小数字。E全1M全0代表无穷大M非0代表非数字NaN。三、整数与浮点数存储核心区别类型存储方式是否有偏移精度特点整数补码直接存储无偏移精确无误差浮点数符号指数尾数指数偏移存在精度丢失四、经典实战案例整数与浮点数互解析为了直观体现整数和浮点数存储规则的巨大差异这里放上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; }1、代码运行结果n的值为9 *pFloat的值为0.000000 n的值为1091567616 *pFloat的值为9.0000002、底层原理剖析① int n 9整数9在内存中以补码存储二进制为00000000 00000000 00000000 00001001。此时强行用float指针读取编译器不会改动二进制只会按照IEEE754浮点规则解析这串二进制最终算出的浮点值无限趋近于0所以打印0.000000。② *pFloat 9.0把9.0存入该内存空间编译器按照浮点规则编码二进制存放的是符号位、指数位、尾数位组合后的二进制。此时再用int类型读取这串浮点二进制无法识别浮点编码规则会直接当作整型补码解析得到一串杂乱大数字1091567616。3、案例总结同一串二进制解析规则不同结果完全不同。整数看补码浮点数看IEEE754编码这也是二者不能随意混用、强制转换结果诡异的根本原因。五、浮点数精度丢失问题重点必考在日常编程中我们经常遇到浮点数诡异的判断问题部分小数无法精准存储、判断相等失效。根本原因在于IEEE754 存储规则 二进制进制转换缺陷。1、浮点数不能精确存储的底层原因计算机底层只认识二进制0和1。整数可以完美转换成二进制但是部分十进制小数无法转换成有限二进制小数。十进制小数转二进制规则乘2取整顺序排列。举个例子十进制0.10.1 进行乘2取整运算后得到的二进制是无限循环小数0.0001100110011……而 float、double 的尾数位长度有限只能截取前面一部分二进制进行保存末尾直接截断舍弃。被舍弃的微小误差就是精度丢失的来源。通俗总结十进制有无限循环小数二进制同样也有。无限的二进制小数塞不下有限的内存空间只能截断产生误差。2、经典代码案例0.1 0.2 0.3#include stdio.h int main() { if (0.1 0.2 0.3) { printf(相等\n); } else { printf(不相等\n); } return 0; }3、代码运行结果输出不相等4、底层原理分析0.1、0.2都是无限循环二进制小数存储时被截断存在微小误差两个带有误差的浮点数相加误差会累积0.3本身同样存在存储误差最终0.10.2 ≈ 0.30000000000000004和 0.3 不是同一个数值判断失效。5、浮点数相等判断的正确改进写法绝对不能使用直接判断浮点数相等。我们只需要判断两个数的差值是否小于一个极小精度范围误差容忍值。#include stdio.h #include math.h // 定义极小精度 #define EPS 1e-6 int main() { double a 0.1; double b 0.2; double c 0.3; // 判断差值绝对值是否小于精度 if (fabs(a b - c) EPS) { printf(相等\n); } else { printf(不相等\n); } return 0; }编写规范float 一般用 1e-5double 一般用 1e-6这是行业通用写法。六、总结1. 整数在内存中以补码存储配合大小端完成字节排布读取无误差。2. 浮点数遵循IEEE754标准拆分符号、指数、尾数存储存在精度丢失。3. 变量首地址永远是低地址大小端只改变字节顺序不改变首地址。4. 整数和浮点数存储逻辑完全不同互相强制转换底层会发生二进制重解析结果往往出乎意料。