数据在内存中的存储

发布于:2024-07-04 ⋅ 阅读:(15) ⋅ 点赞:(0)


在这里插入图片描述

🎯引言

在编程中,理解数据在内存中的存储方式是至关重要的,尤其是在使用低级语言如C语言时。无论是整数、浮点数的存储,还是字节序的概念,都直接影响到数据的正确读取和处理。在本篇博客中,我们将详细探讨整数在内存中的存储方式、大小端字节序及其判断方法,以及浮点数在内存中的存储结构。通过这篇文章,你将深入了解这些基本概念,进而为高效和准确的程序设计打下坚实基础。

👓数据在内存中的存储

1.整数在内存中的存储

1.1 内存中的表示

在C语言中,整数在内存中以二进制形式存储。对于有符号整数,有三种表示方式:原码、反码和补码。理解这些概念对于深入理解计算机如何处理负数和执行算术运算非常重要。

正整数的原码反码补码是相同的 在计算机内存中整数的存储形式是补码

1.1.1 原码(Sign-Magnitude)

原码表示法最简单直接,最高位(最左边的一位)表示符号位,其他位表示数值大小。符号位为0表示正数,符号位为1表示负数。

下面我们都通过int型 也就是4字节 32位来进行举例

例如,32位整数的原码表示:

  • +500000000 00000000 00000000 00000101
  • -5 : 10000000 00000000 00000000 00000101

缺点:原码表示法有两个0(+0-0),这会带来不必要的复杂性。

1.1.2 反码(Ones’ Complement)

反码表示法是对原码的一种改进,用于解决两个0的问题。正数的反码与原码相同,负数的反码通过对原码按位取反得到,即将原码的每一位取反除符号位(0变1,1变0)。

例如,32位整数的反码表示:

  • +500000000 00000000 00000000 00000101
  • -511111111 11111111 11111111 11111010
  • 缺点:反码表示法虽然解决了两个0的问题,但仍然存在加减法运算的复杂性。
1.1.3 补码(Two’s Complement)

补码表示法是现代计算机系统中普遍采用的表示方式,解决了原码和反码的所有缺点。正数的补码与原码相同,负数的补码通过对原码按位取反后加1得到。

例如,8位整数的补码表示:

  • +500000000 00000000 00000000 00000101
  • -511111111 11111111 11111111 11111011

具体计算负数补码的方法:

  1. 将除符号位的其他部分按位取反。
  2. 在反码的基础上加1。

例如,计算-5的补码:

  1. -5的原码:10000000 00000000 00000000 00000101
  2. 按位取反(得到反码):11111111 11111111 11111111 11111010
  3. 加1(得到补码):11111111 11111111 11111111 11111011

优点:补码表示法解决了两个0的问题,简化了加减法运算,并且方便硬件电路实现。

1.2 例子

假设我们有一个8位的有符号整数-18,我们将其表示为原码、反码和补码:

  • 18的原码: 00000000 00000000 00000000 00010010
  • -18的原码:10000000 00000000 00000000 00010010
  • -18的反码:11111111 11111111 11111111 11101101(除符号位原码按位取反)
  • -18的补码:11111111 11111111 11111111 11101110(反码加1)

1.3 补码的加减法

在补码表示法中,加法和减法可以统一处理,这也是补码的一个重要优点。例如,计算5 + (-5)

  • 5的补码:00000000 00000000 00000000 00000101
  • -5的补码:11111111 11111111 11111111 11111011

相加结果:00000000 00000000 00000000 00000101 + 11111111 11111111 11111111 11111011 = 100000000 00000000 00000000 00000000(去掉最高位的进位)

结果为:00000000 00000000 00000000 00000000(即0)

总结

原码、反码和补码是整数在内存中的三种表示方法,其中补码是最常用的表示法,因其有效解决了符号位引起的复杂性问题,并且便于计算机硬件实现和运算。理解这些概念对于编写高效、正确的C语言程序非常有帮助。希望这些内容能帮助你更好地理解整数在内存中的存储机制。

2.大小端字节序和字节序判断

2.1大小端字节序的概念

在计算机体系结构中,字节序(Byte Order)指的是多字节数据在存储器中的排列顺序。具体来说,就是指多字节数据的最低有效字节(Least Significant Byte,简称LSB)和最高有效字节(Most Significant Byte,简称MSB)的存放顺序。

  • 大端字节序(Big-Endian):在大端字节序中,数据的高位字节(MSB)存储在低地址,低位字节(LSB)存储在高地址。这种方式类似于我们阅读数字时从左到右的顺序。
  • 小端字节序(Little-Endian):在小端字节序中,数据的低位字节(LSB)存储在低地址,高位字节(MSB)存储在高地址。这种方式与大端相反,类似于我们阅读数字时从右到左的顺序。

下面我们通过调试的方式,观察以下vs中,是使用大端字节序还是小端字节序存储

int main()
{
	int a=10;//观察a在内存的存储的字节序

	return 0;
}

操作方法如图示:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.2字节序判断

代码示例:

#include <stdio.h>

int main()
{
	int num = 1;
	//通过指针,取出num第一个字节中存储的值
	char* ptr = (char*)&num;
	if (*ptr == 1) {
		printf("小端字节序\n");
	}
	else {
		printf("大端字节序\n");
	}

	return 0;
}

这段代码的核心思想是利用一个整数变量 num 和一个指向它的 char 类型指针 ptr 来判断当前系统的字节序(是小端还是大端)。通过检查 *ptr 的值是否等于 1,可以推断出整数 num 在内存中的存储顺序,从而确定系统使用的是小端字节序还是大端字节序。

3.浮点数在内存中的存储

IEEE 754标准

  • IEEE 754定义了单精度(32位)和双精度(64位)浮点数的存储格式和运算规则。
  • 单精度浮点数由32位组成,包括1位符号位、8位指数位和23位尾数位。
  • 双精度浮点数由64位组成,包括1位符号位、11位指数位和52位尾数位。

存储结构

  • 符号位(Sign):1位,表示正负号,0表示正数,1表示负数。
  • 指数位(Exponent):8位(单精度)或11位(双精度),表示数值的指数部分。
  • 尾数位(Fraction/Mantissa):23位(单精度)或52位(双精度),表示数值的有效位数部分。

图示:

单精度:

在这里插入图片描述

双精度:

在这里插入图片描述

任意一个二进制浮点数V可以表示成如下形式:

V=(-1)s *M*2E

V:浮点数的值。

s:符号位,表示浮点数的正负。当s为0时,表示正数;当s为1时,表示负数。

M:尾数(Mantissa),也称为尾数部分或者系数。它是一个二进制小数,通常表示为1.xxxxx形式,其中xxxxx为二进制小数部分。

E:指数(Exponent),表示浮点数的指数部分。它用来指定浮点数的放大倍数,通常以二进制补码形式表示。

浮点数在内存中的二进制形式转换成V=(-1)s *M*2E示例:

当我们用IEEE 754标准表示浮点数时,公式 V = (-1)^s * M * 2^E 具体如何应用呢?让我们用一个具体的例子来说明:

假设我们要表示单精度浮点数 V,它的32位二进制表示为 01000000010010001111010111000011

根据IEEE 754标准:

  • 符号位 s 是第一个位,即 0,表示正数。
  • 尾数 M 是接下来的23位,即 10010001111010111000011
  • 指数 E 是接下来的8位,即 10000000

现在我们将这些部分代入公式 V = (-1)^s * M * 2^(E - bias),其中 bias 是指数的偏移量(在单精度浮点数中是 127):

  1. 符号位 s
    • s = 0,表示正数。
  2. 尾数 M
    • M = 1.10010001111010111000011(在IEEE 754中,尾数M以1.xxxxx形式存储,其中xxxxx为23位二进制小数部分,首位默认为1,不显式存储)。
  3. 指数 E
    • E = 10000000
    • 计算V
  • 将符号位、尾数和指数部分代入公式:
  • V = (-1)^0 * 1.10010001111010111000011 * 2^(10000000 - 127)
  • 计算指数部分: 10000000 - 127 = 128
  • 因此, V = 1 * 1.10010001111010111000011 * 2^1

指数E的计算规则:

偏移值(Bias)

  • 对于单精度浮点数(32位),偏移值是 127。
  • 对于双精度浮点数(64位),偏移值是 1023。

指数的存储

  • 指数 E 是一个无符号整数,用固定位数表示。在单精度浮点数中,它占据8位,而在双精度浮点数中,它占据11位。
  • 存储时,实际的指数值通过加上偏移值来表示。

计算公式

  • 当读取浮点数时,从存储的二进制值中取出指数部分 E。
  • 然后,通过以下计算来得到实际的指数值:
    • 实际指数值 Eactual= E−偏移值

举例

  • 对于单精度浮点数

    01000000 01001000 11101011 10000110
    
    • 符号位 s0,表示正数。
    • 尾数 M1.010001000111010111000110
    • 指数 E10000000
    • 计算实际指数值: Eactua= 128 - 127 =10000000(二进制)−127(十进制)=128−127=1。
    • 因此,指数部分 E 对应的实际值为 1。

单精度浮点数存储示例:

  1. 转换为二进制

    • 首先,将 3.14 转换为二进制。整数部分和小数部分分别转换:
      • 整数部分 311
      • 小数部分 0.140.00100011110101110000101

    所以,3.14 的二进制表示是 11.00100011110101110000101

  2. IEEE 754规范

    • 符号位0,表示正数。
    • 指数部分:根据IEEE 754规范,指数部分需要进行偏移存储。对于单精度浮点数,偏移值为 127。所以实际的指数部分计算为:
      • 指数值 E = 128 (因为 127 + 1 = 128)。
      • 转换为8位二进制 10000000
    • 尾数部分:根据IEEE 754规范,尾数部分以1.xxxxx形式存储,其中 xxxxx 是小数部分的二进制表示,但首位默认为1,不显式存储。尾数部分为 00100011110101110000101
  3. 组合成32位单精度浮点数

    • 将符号位、指数部分和尾数部分组合起来得到单精度浮点数的二进制表示:

      符号位:0
      指数部分:10000000
      尾数部分:00100011110101110000101
      
    • 结合起来得到完整的32位二进制表示:01000000010010001111010111000011

这就是单精度浮点数 3.14 在内存中按照IEEE 754标准存储的过程和具体二进制表示。

特殊值和异常

  • :整数部分和小数部分都为0。
  • 无穷大:指数部分全为1,尾数部分全为0。
  • NaN(Not a Number):指数部分全为1,尾数部分非全0。

字节序

  • 浮点数的字节序与整数类似,通常由硬件平台决定。在同一硬件平台上,浮点数的字节序通常与整数相同。

🥇结语

通过本篇博客的学习,我们详细探讨了整数和浮点数在内存中的存储方式,大小端字节序的概念及判断方法。这些基础知识对于编写高效、稳定的程序至关重要,尤其是在处理跨平台数据交换时,理解字节序可以帮助避免许多潜在的错误和问题。希望本篇文章能够帮助你更好地理解数据在内存中的存储机制,从而提升你的编程能力和代码质量。感谢你的阅读,欢迎在评论区分享你的问题和见解,让我们共同进步!