C语言中有许多数据类型,使用类型的不同决定了所开辟的空间大小(大小决定了使用范围)及如何看待内存空间的视角,那么今天我们就来一起从0开始学习C语言中整形与浮点型的存储方式,(看完从小白秒变高手!!!):
目录
这里关于补码额外说几点(加深理解部分,可跳过,不影响后续阅读)
整形数据的存储(int、long等)
首先我们先来明确以下几个概念:原码、反码、补码
计算机中的有符号数(整形)有三种表示方式:原码、反码、补码
(三种表示方式均由符号位与数值位两部分构成,符号位(首位)用0表示正数,符号位用1表示负数)
原码:将数字直接按照二进制的翻译方法翻译过来即可
反码:原码的符号位不变,其他位按位取反即可
补码:给原码+1即为补码
·对于无符号数(可以理解为正整数)来说原码、反码、补码都相同
而计算机存储的都是整形数据的补码
接下来,我们以整形a为例:
int a = -10;
那么a的原码为
10000000 00000000 00000000 00001010
11111111 11111111 11111111 11110101 -- 反码
11111111 11111111 11111111 11110110 -- 补码
接下来转化为十六进制则为
0xFFFFFFF6
则a在内存中的存储方式为:
F6 FF FF FF
(一般为小端存储,大小端存储部分内容过两天写完放主页)
这里关于补码额外说几点(加深理解部分,可跳过,不影响后续阅读)
为什么要用这种看似复杂的转化来表示负数呢?
其实是与计算机的组成有关,计算机的CPU只能运算加法,那么当遇到减法的时候怎么办呢,于是就有人提出了补码的概念,将一个负数求出补码后,直接相加,这样就巧妙规避了减法的情况。
但我们完全可以对补码做更深一步的理解以提高我们的基本功:
#include<stdio.h>
int main()
{
char a[100];//创建一个有100个元素的char类型数组
int i = 0;
for(i = 0; i< 1000; i++)//i从1自增到999
{
a[i] = -1 - i;//给每个-i都减-1并存入数组中
}
return o;
}
该程序有趣的点在于数组a中究竟有几个元素,元素分别是什么?
char类型占据一个字节,即8个比特位,那么就意味着存储范围只能是-128到127
for循环内部可以看作a[i]从-1逐渐自减的过程,那么,a[i]在自减至-128时,再自减变为-129,-129却又超出了char类型的存储范围,显然不成立,我们接下来来讨论-128 - 1的这个过程:
10000000000000000000000010000000 -- 负128的原码
11111111111111111111111101111111 -- 负128的反码
11111111111111111111111110000000 -- 负128的补码
给补码加上-1
11111111111111111111111111111111 -- 负一的补码
与负128的补码直接相加得:
11111111111111111111111101111111 -- 得到了-129的补码
在进行后8位截断(关于整形运算中的整型提升与截断过两天我写出来放主页)
01111111
此时我们发现符号位为0,也就是说该数此时被认为成正数,正数原码、反码、补码都相同,所以直接翻译为127,再进行自减,自减至0时(‘/0‘的ASCII码为0),字符串结束,程序终止。
此时程序虽然已经终止,但我们不妨设想以下如果它继续运行,将会发生什么,0 - 1 = -1,接下来-1继续进行自减,也就是说自减的过程相当于一个首尾相接的圈子,又0开始减一,减到-128时再减会变为127,再逐步减回0,同样相加也会逐步加回0。
int型的存储也会是像这样一样“反反复复”,有兴趣的朋友可以自己证明,本文不再做过多赘述。
看到这里不知道你对补码的认知是否又深刻了一步呢?
浮点型的存储方式(float double等)
浮点型在计算机中的存储方式如下:
(-1)^S * M * 2^E
S用于表示该数的正负(0或1)
M用于表示该数的二进制科学计数法有效数字(1<=M<2)
E用于表示幂次
(该表示方法其实等价于十进制中的科学计数法,M是有效位,2^E代表在二进制中小数点的移动操作等效于十进制数中的10^N。)
例如数字9:
我们首先将9写成上述标准形式
9 = (-1)^0 * 1.001*2^3
接下来我们对32个bit位以1、8、23区间长度进行划分(float为4个字节,32个bit位)
0 00000000 00000000000000000000000
·第一个比特位用来存放s(表示数字的正负,相当于符号位)
·第2到9个比特位用来存放E
(在存放E的时候我们需要注意,因为次数项有可能为负数,但是这八个比特位规定是以无符号数的形式存储,所以次方项我们通常采取将给原数+127的方式得到的值存储,翻译时再减去127)
·其余的23位存储有效值(小数部分用0向后补位)
(关于有效位的存储,因为M的值一定是大于等于1小于2的,所以我们不去存储有效数字小数点前的那一位1,只取小数点后的数字存储)
则9以浮点数的形式存储的真实情况为
0 10000010 00100000000000000000000
转化为十六进制即为 41 10 00 00
再按照小端存储的方式放入内存中:00 00 10 41
这里关于E有两种特殊情况:
1、若存放E的8个bit位全为0,则我们认为E的真实值为1 - 127而不是-127,并且在翻译时不再加上有效位中的1,而是表示为0.xxxxxxx,此法则是为了表示0或非常小的数。
2、若存放E的八位全为1,且存放M的23位均为0,此法则是为了表示正负无穷大的数。
注:以上情况默认为float类型32位,若是64位则E的位数变为11位,其余逻辑相同,这里不再做过多说明。
---------------------------------------------------------------------------------------------------------------------------------
有不同理解的朋友欢迎在评论区留言,不足的地方也希望大家帮忙纠正,本系列将会持续更新,刚入门的朋友们可以跟随文章一步步变强,码字不易,喜欢本文的话请记得点赞,祝我们都有美好的未来!!!!!!!!!!!!!!