进阶C语言 第一章-------《数据类型》 (整形内存中的存储、浮点型在内存中的存储)知识点+基本练习题+深入细节+通俗易懂+完整思维导图+建议收藏(二)

简介: 进阶C语言 第一章-------《数据类型》 (整形内存中的存储、浮点型在内存中的存储)知识点+基本练习题+深入细节+通俗易懂+完整思维导图+建议收藏(二)

2.3大小端

知识点:

大小端问题:当我们有一个数据,此时根据类型开辟一个内存空间,就需要有一定的规则规定其放进这个内存空间中去(这个规则就是大小端问题)

image.png

2.3.1大端:

又称大端字节序存储,是指数据的低位保存在内存的高地址中,而数据的高位保存在内存的低地址中;我来将其更简单的概述就是:不用进行什么改变,就和补码保持一致的放进内存中。

2.3.2小端:

又称小端字节序存储,是指数据的低位保存在内存的低地址中,而数据的高位保存在内存的高地址中。简单来说:小端存储即使是将原本的补码反过来放进内存中。

附:大小端存储又称大小端字节序存储的原因是:

因为:在数值中的排序其实是以字节为单位(每两个十六进制表示为一个字节所以同常都是两个两个的十六进制来排)

如-10:0x ff ff ff f6 因为VS环境下是以小端存储的所以其在内存中的存储,所以最终将会表示成 0x F6 FF FF FF (将F6、FF 、... 、FF 这样的一个byte的进行排序 )

而具体的什么数据的低位、高位我在前面写过一篇blog可以在康康

细节:

2.3.3那为什么我们一定需要大小端存在呢?

为什么会有大小端模式之分呢?这是因为在计算机系统中,我们是以字节为单位的,每个地址单元都对应着一个字节,一个字节为8bit。但是在C语言中除了8 bit的char之外,还有16 bit的short型,32 bit的long型(要看具体的编译器),另外,对于位数大于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如何将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式。

例如:一个 16bit 的 short 型 x ,在内存中的地址为 0x0010 , x 的值为 0x1122 ,那么 0x11 为高字节, 0x22 为低字节。对于大端模式,就将 0x11 放在低地址中,即 0x0010 中,0x22 放在高地址中,即 0x0011 中。小端模式,刚好相反。我们常用的 X86 结构是

小端模式,而 KEIL C51 则为大端模式。很多的ARM,DSP都为小端模式。有些ARM处理器还可以由硬件来选择是大端模式还是小端、模式。

简单的概括来说就是:

当我们一个数据的类型所需的空间(int :4byte)要大于一个地址所控制的内存空间(1byte)时我们就需要有一个排序方法,来将还存的int所需的4byte进行排序

练习:

百度2015年系统工程师笔试题:

https://blog.csdn.net/ZYK069/article/details/128620337?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22128620337%22%2C%22source%22%3A%22ZYK069%22%7D

通过代码判断大小端

3.浮点型的内存存储

3.1 浮点型的存储规则

知识点:

常见的浮点型:

3.14159

1E10  (1.0 * 10 ^ 10)

细节:

3.1.1浮点型的存储规则

根据国际标准IEEE(电气和电子工程协会) 754,任意一个二进制浮点数V可以表示成下面的形式:

(-1)^S * M * 2^E(SME)

(-1)^s表示符号位,当s=0,V为正数;当s=1,V为负数。

M表示有效数字,大于等于1,小于2。

2 ^ E表示指数位

例如:如十进制     5.5

写成二进制就可以表示成二进制   101.1  

小数点前面的好理解就是 5 的二进制表示,而小数点后一位就当写上1时就表示成  2 ^ -1 == 0.5  (看2的权重)所以在小数点后加一个1

若要写成(-1)^S * M * 2^E形式时就需要先写成 : 1.011 * 2 ^ 2 (这里对应十进制缩进小数点时的情况)

所以就可以再写成              (-1)^ 0 *  1.011 * 2 ^ 2  

所以套回原来的公式就可以写成:S = 0  ; M = 1.011  ; E = 2    (SME)

所以我们可以发现所有的浮点数写出来最后都只是SME不一样

所以我们只需要知道了一个浮点数的SME得出来就可以得出这个浮点数(故我们只需要将SME存起来后面用时就可以取出这个SME然后得到这个浮点数)

再如:十进制:0.5  

-> 0.1

-> 1.0 * 2 ^ -1  

-> (-1) ^ 0 * 1.0 * 2 ^ -1  

-> S = 0 ; M = 1.0 ;E = -1;

知道了 SME 后我们就需要知道如何将 SME 存进内存中

IEEE 754规定:

对于32位的浮点数,最高的1位是符号位s,接着的8位是指数E,剩下的23位为有效数字M。

对于64位的浮点数,最高的1位是符号位S,接着的11位是指数E,剩下的52位为有效数字M。

用图形表示:

image.png

此处可以将SME 记作 SEM进行存储

现在当我们知道了SME就可以进行存储了,其各个都有不同的存储方式,下面我将一一道来:

S:是1就直接存1,是0进存0,进到S区即可

E:

首先E为一个无符号的整形 ,所以 当 E 为 8位 时 其取值范围是 0 ~ 255 为 11位 是其取值范围是 0 ~2047                    

但是我们知道 E 他可能是一个负值,所以当我们将E存进内存中时要先加上一个中间值(来确保到时候取出来是仍然是一个负数),如32位时要加上一个 127 在存放进E的内存空间中、而64位时要加上一个 1023 在存放进E的内存空间中

如:若E = -1  

32位存放时: -1 + 127 = 126 再将 126 的二进制放进 8bit 的空间中

64位存放时: -1 + 1023 = 1022 再将1022的二进制存放进 11bit 的空间中

当然在我们取出时就需要再把之前所加上的值减去;下面是取E的情况

分为三种:

内存中的E全为0时:                                                                                                     此时因为原E是加上127为0的所以原E为-127,想想这是一个多么小的数字呀 2 ^ -127 要知道 2 ^ 32 已经是42亿多了 还要在不断的乘上近百个二再取倒数,这将是一个无限接近于0的很小的数字,所以此时我们取出的M将不在加1而是直接表示出来为     0. xxxxxx (又因为double、float他们的有效位数有限所以就会导致截断后直接变成0)

内存中的E全为1时:                                                                                                         此时同上因为是+127后才为全1的所以原E应为128(255-127),表示这个数无穷大(不在过多讨论)

内存中的E不是以上情况时(有0 有1):                                                                    此时就是将原存进的E-127/E-102即可取出

M:对于M来说,他是一个 大于等于1 小于2的数即 1. xxxxx 。所以在我们存储的时候就先暂时抛弃 1.  将小数点后面的数存进内存中 ,这样我们就可以节省一定的内存空间从而然这个浮点数更精准(可以提高精度)

如读取M = 1.01时 就先抛弃1.(后面再加上1即可)即存  01  

将01放内存空间的最前面,后面不够的补0

:01000000....

练习:

现在我们再将5.5当作例子进行举例

int main()
{
    float a  = 5.5f;
  //  101.1
  // s = 0 ; m = 1.011 ; e = 2
  // s直接存 ; e加上127(2+127 == 129) ;先去掉1. 再将011存进后面剩余的补0
  // 0 1000 0001 011 0000000000 0000000000
  // 40     b0   00    00
  //vs是小端即为 00 00 b0 40
    return 0;
}

image.png

所以在我们要拿出时就是先将s :0拿出 ;然后是e :此时为第三种情况所以129 -127 = 2  : 2^2

m:011那出再加上1 : 1.011  

最终得到:(-1)^ 0  * 1.011 * 2 ^ 2

也就是:101.1

本章完。预知后事如何,暂听下回分说。

相关文章
|
1月前
|
存储 编译器 C语言
C语言存储类详解
在 C 语言中,存储类定义了变量的生命周期、作用域和可见性。主要包括:`auto`(默认存储类,块级作用域),`register`(建议存储在寄存器中,作用域同 `auto`,不可取地址),`static`(生命周期贯穿整个程序,局部静态变量在函数间保持值,全局静态变量限于本文件),`extern`(声明变量在其他文件中定义,允许跨文件访问)。此外,`typedef` 用于定义新数据类型名称,提升代码可读性。 示例代码展示了不同存储类变量的使用方式,通过两次调用 `function()` 函数,观察静态变量 `b` 的变化。合理选择存储类可以优化程序性能和内存使用。
148 82
|
12天前
|
C语言
C语言学习笔记-知识点总结上
C语言学习笔记-知识点总结上
55 1
|
11天前
|
存储 C语言
深入C语言内存:数据在内存中的存储
深入C语言内存:数据在内存中的存储
|
1月前
|
存储 人工智能 C语言
数据结构基础详解(C语言): 栈的括号匹配(实战)与栈的表达式求值&&特殊矩阵的压缩存储
本文首先介绍了栈的应用之一——括号匹配,利用栈的特性实现左右括号的匹配检测。接着详细描述了南京理工大学的一道编程题,要求判断输入字符串中的括号是否正确匹配,并给出了完整的代码示例。此外,还探讨了栈在表达式求值中的应用,包括中缀、后缀和前缀表达式的转换与计算方法。最后,文章介绍了矩阵的压缩存储技术,涵盖对称矩阵、三角矩阵及稀疏矩阵的不同压缩存储策略,提高存储效率。
221 8
|
18天前
|
存储 C语言
C语言中的浮点数存储:深入探讨
C语言中的浮点数存储:深入探讨
|
存储 C语言
【C语言】四行代码说明浮点型在内存中的储存
【C语言】四行代码说明浮点型在内存中的储存
【C语言】四行代码说明浮点型在内存中的储存
|
10天前
|
C语言 C++
C语言 之 内存函数
C语言 之 内存函数
25 3
|
1天前
|
存储 缓存 C语言
【c语言】简单的算术操作符、输入输出函数
本文介绍了C语言中的算术操作符、赋值操作符、单目操作符以及输入输出函数 `printf` 和 `scanf` 的基本用法。算术操作符包括加、减、乘、除和求余,其中除法和求余运算有特殊规则。赋值操作符用于给变量赋值,并支持复合赋值。单目操作符包括自增自减、正负号和强制类型转换。输入输出函数 `printf` 和 `scanf` 用于格式化输入和输出,支持多种占位符和格式控制。通过示例代码详细解释了这些操作符和函数的使用方法。
17 10
|
4天前
|
存储 编译器 C语言
C语言函数的定义与函数的声明的区别
C语言中,函数的定义包含函数的实现,即具体执行的代码块;而函数的声明仅描述函数的名称、返回类型和参数列表,用于告知编译器函数的存在,但不包含实现细节。声明通常放在头文件中,定义则在源文件中。
|
11天前
|
C语言
c语言回顾-函数递归(上)
c语言回顾-函数递归(上)
27 2