目录
一、整数在内存中的存储
1.存储形式
2.原码、反码和补码
二、大小端字节序和字节序判断
1.什么是大小端?
2.为什么有大小端?
3.判断大小端模式
三、浮点数在内存中的存储
1.奇怪的现象
2.浮点数内存存储
2.1指数E的特殊情况
3.回到例题
往期内容:
一、整数在内存中的存储
1.存储形式
整型数据在内存中存储的是二进制的补码形式,这是计算机中表示整数的标准方式
2.原码、反码和补码
整数的2进制表⽰⽅法有三种,即原码、反码和补码
有符号整数的三种表⽰⽅法均有符号位和数值位两部分,2进制序列中,最⾼位的1位是被当做符号 位,剩余的都是数值位。
符号位都是⽤0表⽰“正”,⽤1表⽰“负”
int 类型来说,占 4 个字节,32 个 bit 位
正整数的原、反、补码都相同。
负整数的三种表示方法各不相同。
原码:直接将数值按照正负数的形式翻译成⼆进制得到的就是原码。
反码:将原码的符号位不变,其他位依次按位取反就可以得到反码。
补码:反码+1就得到补码。 补码得到原码也是可以使⽤:取反,+1的操作。
⽆符号整数的三种 2 进制表示相同(直接将其数值转换为二进制存储),没有符号位,每⼀位都是数值位。
举个栗子
对于正数,它的原码、反码和补码是完全相同的。所以,
+25的二进制直接表示即可。对于负数,转换则遵循特定的步骤(以 -25 为例):
原码:最高位(符号位)为
1,表示负数。其余位是该数绝对值(25)的二进制表示。所以,-25 的原码是10000000 00000000 00000000 00011001。反码:保持符号位
1不变,将原码的数值位(0000000 00000000 00000000 0011001)按位取反(0变1,1变0),因此,-25 的反码是11111111 11111111 11111111 11100110。补码:在反码的基础上,最低位加1。反码
11111111 11111111 11111111 11100110加 1 后得到11111111 11111111 11111111 11100111。
| 数值 | 原码 | 反码 | 补码 |
| +25 | 00000000 00000000 00000000 00011001 | 00000000 00000000 00000000 00011001 | 00000000 00000000 00000000 00011001 |
| -25 | 10000000 00000000 00000000 00011001 | 11111111 11111111 11111111 11100110 | 11111111 11111111 11111111 11100111 |
对于整形来说:数据存放内存中其实存放的是⼆进制的补码,那这是为什么呢?
在计算机系统中,数值⼀律⽤补码来表⽰和存储。
原因在于,使⽤补码,可以将符号位和数值域统⼀处理;
同时,加法和减法也可以统⼀处理(CPU只有加法器)
此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。
例如,计算
1 - 1相当于计算1 + (-1):
1的补码:00000000 00000000 00000000 00000001
-1的原码:10000000 00000000 00000000 00000001反码:
11111111 11111111 11111111 11111110补码:
11111111 11111111 11111111 11111111两者补码相加:
结果是:1 00000000 00000000 00000000 00000000最前面的进位溢出舍弃,最终得到
0,结果正确。
二、大小端字节序和字节序判断
当我们了解了整数在内存中存储后,我们调试看⼀个细节:
int main() { int a = 0x11223344; return 0; }调试的时候,我们可以看到在a中的 0x11223344 这个数字是按照字节为单位,倒着存储的。这是为什么呢?
1.什么是大小端?
其实超过⼀个字节的数据在内存中存储的时候,就有存储顺序的问题,按照不同的存储顺序,我们分为大端字节序存储和小端字节序存储,下面是具体的概念:
大端(存储)模式: 是指数据的低位字节内容保存在内存的⾼地址处,而数据的高位字节内容,保存在内存的低地址处。
小端(存储)模式: 是指数据的低位字节内容保存在内存的低地址处,而数据的高位字节内容,保存在内存的高地址处。
2.为什么有大小端?
这是因为在计算机系统中,我们是以字节为单位的,每个地址单元都对应着⼀个字节,⼀个字节为8 bit 位,但是在C语⾔中除了8 bit 的 char 之外,还有16 bit 的 short 型,32 bit 的 long 型(要看 具体的编译器),另外,对于位数大于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于⼀个字节,那么必然存在着⼀个如何将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式。
3.判断大小端模式
请简述⼤端字节序和⼩端字节序的概念,设计⼀个⼩程序来判断当前机器的字节序。(10分)- 百度笔试题
#include <stdio.h> 因为int a = 1 ;中的 a 储存的是0x 00 00 00 01 所以可以将 a 内存中第一个字节取出来,判断是 1 还是 0 是 1 就是小端模式,否则是大端模式 int check_sys() { int i = 1; return (*(char *)&i); } int main() { int ret = check_sys(); if(ret == 1) { printf("⼩端\n"); } else { printf("⼤端\n"); } return 0; }还有一种联合体的写法
int check_sys() { union { int i; char c; }un; un.i = 1; return un.c; }三、浮点数在内存中的存储
常⻅的浮点数:3.14159、1E10(1E10的完整数学表达式是 1 × 10¹⁰)等,浮点数家族包括: float、double、long double 类型。 浮点数表示的范围: float.h 中定义。
1.奇怪的现象
大家可以猜猜代码的运行结果嘻嘻:
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; }是不是很出乎意料,上⾯的代码中, n 和 *pFloat 在内存中明明是同⼀个数,为什么浮点数和整数的解读结果会差别这么⼤?
在这个过程中,n就相当于一块空间,可以以整数的视角来理解这4个字节,也可以以浮点数的视角来理解这4个字节,以整数的形式存放,然后以整数的形式拿出,以浮点数的形式存放,然后以浮点数的形式拿出,但是我以整数的形式存放,以浮点型的方式拿出,以浮点数的形式存放,以整型的方式拿出,结果就不对,由此说明,整数和浮点数在内存中的存放方式不同
2.浮点数内存存储
要理解这个结果,⼀定要搞懂浮点数在计算机内部的表示⽅法。
根据国际标准IEEE(电⽓和电⼦⼯程协会) 754,任意⼀个⼆进制浮点数V可以表⽰成下⾯的形式:
V = (-1)^S * M * 2^E
S 表示符号位,当S=0,V为正数;当S=1,V为负数
M 表示有效数字,是一个大于等于1且小于2的二进制小数。例如,二进制下的
1.011E 表示2的幂次,决定小数点的位置
举例来说:
⼗进制的5.0,写成⼆进制是 101.0 ,相当于1.01* 2^2 。 那么,按照上⾯ V 的格式,可以得出S=0,M=1.01,E=2。
IEEE 754标准主要定义了两种精度的浮点数格式,它们在内存中的位分配如下
| 精度 | 总位数 | 符号位 (S) | 指数位 (E) | 尾数位 (M) |
| 单精度 (float) | 32位 | 1位 | 8位 | 23位 |
| 双精度 (double) | 64位 | 1位 | 11位 | 52位 |
具体规则如下:
符号位 (S):直接存储,0代表正,1代表负
指数 (E):E在存储时是一个无符号整数。为了能表示负指数,实际存储的是
例如,单精度下真实指数E=2,存储时就是2+127=129,二进制为10000001E + 偏移值,单精度的偏移值是127,双精度是1023有效数字 (M):由于M总是
1.XXXXXX的形式,整数部分的1是固定的,因此存储时只保存小数部分XXXXXX,这样可以多出一位有效数字,提高精度,读取时再将这个1加上
我们以单精度浮点数存储17.625为例
转换二进制:整数部分
17转二进制为10001;小数部分0.625转二进制为0.101(因为0.625 =0.5+0.125,即2^(-1) + 2^(-3)),所以17.625的二进制是10001.101规范化:将二进制数小数点左移4位,变为
1.0001101 × 2^4。得到S=0,M=1.0001101,E=4。计算存储值:符号位 S:
0,阶码 E:4+127=131,二进制为10000011,尾数 M:存储M的小数部分0001101,后面用0补足23位,得到00011010000000000000000组合:最终32位序列为
0 10000011 00011010000000000000000。通常我们表示为十六进制0x41 8D 00 00
这样的浮点数存储⽅式很巧妙,但是我们也要注意到有的浮点数是⽆法精确保存的。⽐如:1.2,我们可以在VS上调试看⼀下,我们发现会有些许误差
2.1指数E的特殊情况
E全为0
这时,浮点数的指数E等于1-127(或者1-1023)即为真实值,有效数字M不再加上第⼀位的1,而是还原为0.xxxxxx的小数。这样做是为了表示±0,以及接近于0的很小的数字
0 00000000 00100000000000000000000
E全为1
这时,如果有效数字M全为0,表示±无穷大(正负取决于符号位s)
0 11111111 00010000000000000000000
3.回到例题
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; }我用excel表格做了个图解
1,4相信聪明的你肯定知道,我们重点讲一下2,3
第2题,取出n的地址再强制类型转换给指针pFloat,再以浮点数形式打印
第3题,取出 n 的地址再强制类型转换给指针 pFloat ,再通过解引用把 n 的值改为 9.0 (浮点数),最后以整型形式打印
往期内容:
C语言内存函数使用和模拟(memcpy,memmove,memset,memcmp)4000字超详解
C语言分支和循环语句保姆级讲解0基础也能看懂
C语言中传参的本质
OK,以上就是全部内容了
若内容对大家有所帮助,可以收藏慢慢看,感谢大家支持
本文有若有不足之处,希望各位兄弟们能给出宝贵的意见。谢谢大家!!
本期制作不易希望各位兄弟们能动动小手,三连走一走!!
支持一下(三连必回0.0)