新闻详情

新闻详情

首页 / 资讯中心 / 详情

数据在内存中的存储:从整数到浮点数的底层原理

发布时间:2026/7/5 14:52:55
数据在内存中的存储:从整数到浮点数的底层原理
本文目录 1. 整数在内存中的存储 原码、反码、补码的定义 为什么计算机要使用补码 2. 大小端字节序和字节序判断 2.1 什么是大小端 2.2 为什么会有大小端 2.3 经典练习 3. 浮点数在内存中的存储 3.1 一个有趣的练习 3.2 浮点数的存储规则IEEE 754标准 3.3 题目解析 总结与面试题 核心要点总结 经典面试题 1. 整数在内存中的存储在计算机组成原理中我们知道整数的2进制表示方法有三种即原码、反码和补码。有符号的整数三种表示方法均有符号位和数值位两部分符号位都是用0表示“正”用1表示“负”最高位的一位是被当做符号位剩余的都是数值位。正整数的原、反、补码都相同。负整数的三种表示方法各不相同。 原码、反码、补码的定义原码直接将数值按照正负数的形式翻译成二进制得到的就是原码。反码将原码的符号位不变其他位依次按位取反就可以得到反码。补码反码 1 就得到补码。 核心结论对于整形来说数据存放内存中其实存放的是补码。 为什么计算机要使用补码在计算机系统中数值一律用补码来表示和存储。原因在于统一处理符号位和数值域使用补码可以将符号位和数值域统一处理。化减法为加法加法和减法可以统一处理CPU只有加法器例如1 - 1可以转化为1 (-1)用补码计算就不会出错。运算电路一致补码与原码相互转换其运算过程是相同的不需要额外的硬件电路简化了计算机的硬件设计。 2. 大小端字节序和字节序判断当我们了解了整数在内存中存储后我们调试看一个细节#includestdio.hintmain(){inta0x11223344;return0;}调试的时候我们可以看到在a中的0x11223344这个数字是按照字节为单位倒着存储的。这是为什么呢 2.1 什么是大小端其实超过一个字节的数据在内存中存储的时候就有存储顺序的问题按照不同的存储顺序我们分为大端字节序存储和小端字节序存储下面是具体的概念大端存储模式是指数据的低位字节内容保存在内存的高地址处而数据的高位字节内容保存在内存的低地址处。小端存储模式是指数据的低位字节内容保存在内存的低地址处而数据的高位字节内容保存在内存的高地址处。 记忆技巧大端模式更符合我们人类的阅读习惯从左到右高位到低位而小端模式则更符合计算机从低地址开始读取数据的习惯。 2.2 为什么会有大小端为什么会有大小端模式之分呢这是因为在计算机系统中我们是以字节为单位的每个地址单元都对应着一个字节一个字节为 8 bit 位但是在C语言中除了 8 bit 的char之外还有 16 bit 的short型32 bit 的long型要看具体的编译器另外对于位数大于8位的处理器例如16位或者32位的处理器由于寄存器宽度大于一个字节那么必然存在着一个如何将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式。举例说明一个 16bit 的short型x在内存中的地址为0x0010x的值为0x1122那么0x11为高字节0x22为低字节。对于大端模式就将0x11放在低地址中即0x0010中0x22放在高地址中即0x0011中。小端模式刚好相反。我们常用的X86 结构是小端模式而KEIL C51 则为大端模式。很多的ARMDSP都为小端模式。有些ARM处理器还可以由硬件来选择是大端模式还是小端模式。 2.3 经典练习练习1判断当前机器的字节序百度笔试题请简述大端字节序和小端字节序的概念设计一个小程序来判断当前机器的字节序。代码1使用指针类型转换#includestdio.hintcheck_sys(){inti1;return(*(char*)i);}intmain(){intretcheck_sys();if(ret1){printf(小端\n);}else{printf(大端\n);}return0;}代码2使用联合体unionintcheck_sys(){union{inti;charc;}un;un.i1;returnun.c;} 解析int i 1在内存中的十六进制表示为0x00 00 00 01。如果是小端模式低地址存放的是低字节01所以(char*)i取到的值是1如果是大端模式低地址存放的是高字节00取到的值是0。练习2char类型与符号位#includestdio.hintmain(){chara-1;signedcharb-1;unsignedcharc-1;printf(a%d,b%d,c%d,a,b,c);return0;} 解析-1的补码是11111111。char和signed char都是有符号的最高位是符号位所以11111111作为有符号数解读为-1。unsigned char是无符号的11111111作为无符号数解读为255。练习3char类型与整型提升#includestdio.hintmain(){chara-128;printf(%u\n,a);return0;}#includestdio.hintmain(){chara128;printf(%u\n,a);return0;} 解析char类型在打印%u无符号整型时会发生整型提升。-128的补码是10000000提升为int后高位补1变成11111111 11111111 11111111 10000000作为无符号数解读是一个很大的数。128的二进制是10000000对于char来说它超出了-128~127的范围会被编译器当作-128处理结果同上。练习4数组与strlen#includestdio.hintmain(){chara[1000];inti;for(i0;i1000;i){a[i]-1-i;}printf(%d,strlen(a));return0;} 解析char的范围是-128 ~ 127。循环中a[i]的值从-1, -2, -3 ... -128, 127, 126 ... 0。strlen遇到\0即数值0停止。从-1到-128共128个从127到1共127个所以strlen的结果是128 127 255。练习5无符号数的死循环#includestdio.hunsignedchari0;intmain(){for(i0;i255;i){printf(hello world\n);}return0;}#includestdio.hintmain(){unsignedinti;for(i9;i0;i--){printf(%u\n,i);}return0;} 解析unsigned char的范围是0~255当i加到255后再加1会溢出回绕到0导致循环条件i255永远成立形成死循环。同理unsigned int永远0i--到0后再减会变成4294967295也是死循环。练习6指针与内存布局#includestdio.h//X86环境 小端字节序intmain(){inta[4]{1,2,3,4};int*ptr1(int*)(a1);int*ptr2(int*)((int)a1);printf(%x,%x,ptr1[-1],*ptr2);return0;} 解析a 1跳过整个数组指向数组末尾。ptr1[-1]相当于*(ptr1 - 1)即数组最后一个元素4。(int)a 1将数组首地址转为整数后1即地址向后移动一个字节。ptr2指向从数组第二个字节开始的一个int。在小端模式下内存布局为01 00 00 00 02 00 00 00 ...从第二个字节开始读取4个字节得到00 00 00 02即0x02000000。 3. 浮点数在内存中的存储常见的浮点数3.14159、1E10等浮点数家族包括float、double、long double类型。浮点数表示的范围float.h中定义。 3.1 一个有趣的练习#includestdio.hintmain(){intn9;float*pFloat(float*)n;printf(n的值为%d\n,n);printf(*pFloat的值为%f\n,*pFloat);*pFloat9.0;printf(num的值为%d\n,n);printf(*pFloat的值为%f\n,*pFloat);return0;} 思考输出什么为什么n和*pFloat在内存中是同一个数但解读结果差别这么大 3.2 浮点数的存储规则IEEE 754标准要理解这个结果一定要搞懂浮点数在计算机内部的表示方法。根据国际标准IEEE电气和电子工程协会754任意一个二进制浮点数V可以表示成下面的形式V (−1)^S * M * 2^E(−1)^S表示符号位当S0V为正数当S1V为负数。M表示有效数字M是大于等于1小于2的。2^E表示指数位。举例说明十进制的5.0写成二进制是101.0相当于1.01 × 2^2。那么按照上面V的格式可以得出S0M1.01E2。十进制的-5.0写成二进制是-101.0相当于-1.01 × 2^2。那么S1M1.01E2。IEEE 754规定对于32位的浮点数float最高的1位存储符号位S接着的8位存储指数E剩下的23位存储有效数字M。对于64位的浮点数double最高的1位存储符号位S接着的11位存储指数E剩下的52位存储有效数字M。float类型浮点数内存分配double类型浮点数内存分配3.2.1 浮点数存的过程IEEE 754 对有效数字M和指数E还有一些特别规定。关于M前面说过1 ≤ M 2也就是说M可以写成1.xxxxxx的形式。IEEE 754规定在计算机内部保存M时默认这个数的第一位总是1因此可以被舍去只保存后面的xxxxxx部分。比如保存1.01的时候只保存01等到读取的时候再把第一位的1加上去。这样做的目的是节省1位有效数字。以32位浮点数为例留给M只有23位将第一位的1舍去以后等于可以保存24位有效数字。关于E首先E为一个无符号整数unsigned int。这意味着如果E为8位它的取值范围为0255如果E为11位它的取值范围为02047。但是我们知道科学计数法中的E是可以出现负数的所以IEEE 754规定存入内存时E的真实值必须再加上一个中间数对于8位的E这个中间数是127对于11位的E这个中间数是1023。比如2^10的E是10所以保存成32位浮点数时必须保存成10 127 137即10001001。3.2.2 浮点数取的过程指数E从内存中取出还可以再分成三种情况E不全为0或不全为1这时浮点数就采用下面的规则表示即指数E的计算值减去127或1023得到真实值再将有效数字M前加上第一位的1。举例0.5的二进制形式为0.1由于规定正数部分必须为1即将小数点右移1位则为1.0 * 2^(-1)其阶码为-1 127 126表示为01111110而尾数1.0去掉整数部分为0补齐0到23位00000000000000000000000则其二进制表示形式为0 01111110 00000000000000000000000E全为0这时浮点数的指数E等于1 - 127或者1 - 1023即为真实值有效数字M不再加上第一位的1而是还原为0.xxxxxx的小数。这样做是为了表示±0以及接近于0的很小很小的数字。举例0 00000000 00100000000000000000000E全为1这时如果有效数字M全为0表示±无穷大正负取决于符号位s。举例0 11111111 00000000000000000000000 3.3 题目解析下面让我们回到一开始的练习。第1环节为什么9还原成浮点数就成了0.0000009以整型的形式存储在内存中得到如下二进制序列0000 0000 0000 0000 0000 0000 0000 1001首先将9的二进制序列按照浮点数的形式拆分得到第一位符号位s0后面8位的指数E00000000最后23位的有效数字M000 0000 0000 0000 0000 1001。由于指数E全为0所以符合E为全0的情况。因此浮点数V就写成V (-1)^0 × 0.00000000000000000001001 × 2^(-126) 1.001 × 2^(-146)显然V是一个很小的接近于0的正数所以用十进制小数表示就是0.000000。第2环节浮点数9.0为什么整数打印是1091567616首先浮点数9.0等于二进制的1001.0即换算成科学计数法是1.001 × 2^3。所以9.0 (-1)^0 * (1.001) * 2^3。那么第一位的符号位S0有效数字M等于001后面再加20个0凑满23位指数E等于3 127 130即10000010。所以写成二进制形式应该是S E M即0 10000010 001 0000 0000 0000 0000 0000这个32位的二进制数被当做整数来解析的时候就是整数在内存中的补码原码正是1091567616。 总结与面试题 核心要点总结整数存储计算机中整数以补码形式存储这可以统一符号位和数值域并将减法运算转化为加法。大小端多字节数据在内存中的存储顺序。小端模式低地址存低位是主流如X86大端模式低地址存高位常见于网络协议。浮点数存储遵循IEEE 754标准采用(-1)^S * M * 2^E的科学计数法。存储时M的整数部分1被省略以节省空间E需要加上一个中间数127或1023来存储负数指数。浮点数取回根据E的取值分为三种情况正常E不全0/1、表示0或极小值E全0、表示无穷大E全1。 经典面试题Q1请简述大端字节序和小端字节序的概念并设计一个小程序来判断当前机器的字节序。A1大端数据的高位字节存放在内存的低地址处。小端数据的低位字节存放在内存的低地址处。判断程序使用union或指针类型转换检查int类型变量1的第一个字节是1小端还是0大端。Q2char a -128; printf(%u\n, a);的输出结果是什么为什么A2输出结果是4294967168。因为-128的补码是10000000当以%u无符号整型打印时char类型会先进行整型提升符号位为1高位补1变成11111111 11111111 11111111 10000000这个二进制序列作为无符号整数解读就是4294967168。Q3float和int同样占用4个字节为什么int n 9和float *pFloat (float *)n; printf(%f\n, *pFloat);打印出来的结果不是9.0A3因为int和float对内存中同一段二进制数据的解释规则完全不同。int将内存中的00000000 00000000 00000000 00001001直接当作整数的补码解读为9。float则按照 IEEE 754 标准将这32位拆分为S1位、E8位、M23位来解读。对于9的二进制序列E全为0表示这是一个非常接近于0的极小值所以打印结果为0.000000。Q4unsigned char i 0; for(i 0; i 255; i) { printf(hello world\n); }这个循环会执行多少次A4这个循环会无限循环。因为unsigned char的取值范围是0 ~ 255。当i增加到255时满足条件i 255继续执行循环体。执行完i后i会溢出回绕到0循环条件再次成立导致死循环。
网站建设 高端定制 企业官网