『C语言进阶』数据在内存中的存储规则

简介: 『C语言进阶』数据在内存中的存储规则

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站

前言

小羊近期已经将C语言初阶学习内容与铁汁们分享完成,接下来小羊会继续追更C语言进阶相关知识,小伙伴们坐好板凳,拿起笔开始上课啦~


一、数据类型的介绍

我们目前已经学了基本的内置类型:

char       //字符数据类型
short      //短整型
int        //整形
long       //长整型
long long  //更长的整形
float      //单精度浮点数
double     //双精度浮点数

类型的基本归类

  1. 整形家族:
char:
   unsigned char
   signed char
short:
   unsigned short[int]
   signed short[int]
int:
   unsigned int
   signed int
long:
   unsigned long[int]
   signed long[int]

unsigned:无符号数类型

当一个数是无符号类型时,那么其最高位的1或0,和其它位一样,用来表示该数的大小。

signed:有符号数类型

当一个数是有符号类型时,最高数称为“符号位”。符号位为1时,表示该数为负数,为0时表示为正数。

注意:有符号类型可以表示正数,负数或0,无符号类型仅能表示大于等于0的值

  1. 浮点型家族:
float
double
  1. 构造类型:
//数组类型
struct //结构体类型
enum   //枚举类型
union //联合类型
  1. 指针类型:
int* p;
char* p;
float* p;
void* p;
  1. 空类型:
void//(空类型)

二、整型在内存中的存储

以整型int为例,我们都知道常见的编译器中int占四个字节,那么计算机中这四个字节是如何将数据存储下来的呢?

那我们先了解一下机器数和真值的概念,再去了解原码,反码,补码的概念

2.1 机器数

一个数在计算机中的二进制表示形式,叫做这个数的机器数。机器数是带符号的,在计算机中 用机器数的最高位存放符号,正数为0,负数为1。

例如:

+ 3的机器数:0000 0011
- 3的机器数:1000 0011

2.2 真值

因为第一位是符号位,所以机器数的形式值就不等于真正的数值。所以,为区别起见,将带符号位的机器数对应的真正数值称为机器数的真值。

例如:

0000 0001的真值 = +000 0001 = +1
1000 0001的真值 = -000 0001 = -1

2.3 原码、反码、补码

对于一个数,计算机要使用一定的编码方式进行存储,原码、反码、补码是机器存储一个具体数字的编码方式。

三种方式均有符号位和数值位两部分,符号位都是0表示“正数”,1表示“负数”,而数值位分正负数而定。

正数的原码、反码、补码都相同,负数的原码、反码、补码各不相同

原码:

直接将数值按照正负数的形式翻译成二进制就可以得到原码

反码:

将原码的符号位不变,其他位次按位取反

补码:

反码符号位不变,数值为+1

反码回到原码的两种方式:

1、补码-1后 取反得到原码

2、补码取反后 +1得到原码

对于整形来说:数据存放内存中其实存放的是补码,那这又是为什么呢?

在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数

值域统一处理;同时,加法和减法也可以统一处理(CPU只有加法器 )此外,补码与原码相

互转换,其运算过程是相同的,不需要额外的硬件电路。

我们看看在内存中的存储:

我们知道内存中a和b存储的是补码,但我们发现存储的顺序有点不对劲。

-10在内存中存储应该是FFFFFFF6,而我们看到的是F6FFFFFF。

这里小羊呢,就为铁汁们了解一下大小端

2.4 大小端介绍

什么是大小端:

大端存储模式:指数据的低位保存在内存的高地址中,而数据的高位保存在内存的低地址中

小端存储模式:指数据的低位保存在内存的低地址中,而数据的高位保存在内存的高地址中

例如:

数字0x12 34 56 78在内存中:

大端模式:(我们通常直观上认为的模式)

低地址 --------------------> 高地址
         0x12  |  0x34  |  0x56  |  0x78

小端模式:

低地址 --------------------> 高地址
         0x78  |  0x56  |  0x34  |  0x12

** 为什么会有大端和小端呢?**

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

如何判断大小端的代码:

#include<stdio.h>
int main()
{
  int i = 1;//0000 0001
  char* p = &i;
  if (*p == 1)//若第一个地址存的是1,即为小端,反则大端
    printf("小端");
  else
    printf("大端";
  return 0;
}

自定义函数测试:

#include<stdio.h>
int check_sys()
{
  int a = 1;
  char* p = (char*)&a;
  if (*p == 1)
    return 1;
  else
    return 0;
}
int main()
{
  if (check_sys() == 1)
    printf("小端");
  else
    printf("大端");
  return 0;
}

三、浮点数在内存中的存储

#include<stdio.h>
int main()
{
  int n = 9;
  float* p = (float*)&n;
  printf("n的值为:%d\n", n);
  printf("*p的值为:%f\n", *p);
  *p = 9.0;
  printf("n的值为:%d\n", n);
  printf("*p的值为:%f\n", *p);
  return 0;
}

我们先试着猜一下结果

输出显示:

怎么样,这个结果是不是有点出乎意料!那么就跟着小羊来学习浮点数的存储规则吧。

3.1浮点数存储规则

浮点数存储形式:

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

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


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

 2.  M 表示有效数字,且1 <= M <2

 3.  2 ^ E表示指数位

例如:

  1. 十进制的5.0,写成二进制是0101 ------> 1.10x2^2
    可以得出s=0,M=1.01,E=2
  2. 十进制的-7.0,写成二进制是0111 ------->1.11x2^2
    可以得出s=-1,M=1.11,E=2

IEEE 754 规定:

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

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

IEEE 754 对有效数字** M **和指数 E ,还有一些特别规定。

前面说过, 1≤M<2 ,也就是说, M 可以写成 1.xxxxxx 的形式,其中 xxxxxx 表示小数部分。

IEEE 754 规定,在计算机内部保存 M 时,默认这个数的第一位总是 1 ,因此可以被舍去,只保存后面的xxxxxx部分。比如保存 1.01 的时候,只保存01 ,等到读取的时候,再把第一位的 1 加上去。这样做的目的,是节省 1 位有效数字。以 32 位浮点数为例,留给M 只有 23 位,将第一位的1 舍去后等于可以保存 24 位有效数字。

至于指数 E ,情况就比较复杂。首先, E 为一个无符号整数( unsigned int )

这意味着,如果 E 为 8 位,它的取值范围为 0~255 ;如果 E 为 11 位,它的取值范围为 0~2047 。但是我们知道,科学计数法中的E 是可以出现负数的,所以IEEE 754 规定,存入内存时 E 的真实值必须再加上一个中间数,对于 8 位的 E ,这个中间数是127 ;对于 11 位的 E ,这个中间数是1023 。比如 2^10的 E 是 10 ,所以保存成 32 位浮点数时,必须保存成 10+127=137 ,即10001001。

3.2 浮点型的读取

我们知道浮点型在内存中的存储后,将步骤反过来就是取出的过程。

1、有效数字M:

IEEE 754规定,在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存小数部分。比如保存1.0110001101时,只保存0110001101,后面的位数补0就可以了,等到读取的时候,再把第一位的1补上去。

2、指数E

E为一个无符号整数(unsigned int)根据指数域不同取值分为一下三种情况:

1)E不全为0或不全为1(规格化值)

这是最常见情况,取出内存中的数时,指数E的计算值减去127(或1023),得到真实值,再将有效数字M前加上第一位的1。

2)E全为0(非规格化值)

这时,浮点数的指数E等于1-127(或1-1023)即为真实值,有效数字M不再加上第一位的1,而是还原为0.xxxxxxx的小数。这样做是为了表示正负零,以及接近于0的很小的数字。

3)E全为1(特殊数值)

当指数域全为1时属于这种情形。此时,如果小数域全为0且符号域S=0,则表示正无穷,如果小数域全为0且符号域S=1,则表示负无穷。如果小数域不全为0时,浮点数将被解释为NaN, 即不是一个数(Not a Number)

解释前面的题目:

整形9以浮点型打印
整形存储,浮点型打印
0000  0000 0000 0000 0000 0000 0000 1001
浮点型读取:
s=0,M=000 0000 0000 0000 0000 0110,E=0000 0000(E全为0)
所以结果为:0.0000(近于0的很小的数字)
现在看例题的第二部:
浮点数9.0以整形打印
9.0 -> 1001.0 -> (-1)^0*1.001*2^3 -> s=0,M=1.001,E=,3+127=130
所以第一位的符号位s=0,有效数字M为001后面在加20个0,凑满23位,指数E为3+127=130,即10000010
所以写成S+E+M:
0 10000010 001 0000 0000 0000 0000 0000
这32位的二进制数,还原成十进制,正是1091567616

总结

希望看完这篇文章对铁汁们有所帮助,小羊后续还会持续更新C语言的学习知识,希望小伙伴们给个支持,来个一键三连~

相关文章
|
19天前
|
存储 程序员 编译器
C 语言中的数据类型转换:连接不同数据世界的桥梁
C语言中的数据类型转换是程序设计中不可或缺的一部分,它如同连接不同数据世界的桥梁,使得不同类型的变量之间能够互相传递和转换,确保了程序的灵活性与兼容性。通过强制类型转换或自动类型转换,C语言允许开发者在保证数据完整性的前提下,实现复杂的数据处理逻辑。
|
19天前
|
存储 编译器 程序员
【C语言】内存布局大揭秘 ! -《堆、栈和你从未听说过的内存角落》
在C语言中,内存布局是程序运行时非常重要的概念。内存布局直接影响程序的性能、稳定性和安全性。理解C程序的内存布局,有助于编写更高效和可靠的代码。本文将详细介绍C程序的内存布局,包括代码段、数据段、堆、栈等部分,并提供相关的示例和应用。
31 5
【C语言】内存布局大揭秘 ! -《堆、栈和你从未听说过的内存角落》
|
20天前
|
存储 数据管理 C语言
C 语言中的文件操作:数据持久化的关键桥梁
C语言中的文件操作是实现数据持久化的重要手段,通过 fopen、fclose、fread、fwrite 等函数,可以实现对文件的创建、读写和关闭,构建程序与外部数据存储之间的桥梁。
|
22天前
|
传感器 人工智能 物联网
C 语言在计算机科学中尤其在硬件交互方面占据重要地位。本文探讨了 C 语言与硬件交互的主要方法,包括直接访问硬件寄存器、中断处理、I/O 端口操作、内存映射 I/O 和设备驱动程序开发
C 语言在计算机科学中尤其在硬件交互方面占据重要地位。本文探讨了 C 语言与硬件交互的主要方法,包括直接访问硬件寄存器、中断处理、I/O 端口操作、内存映射 I/O 和设备驱动程序开发,以及面临的挑战和未来趋势,旨在帮助读者深入了解并掌握这些关键技术。
40 6
|
23天前
|
存储 数据建模 程序员
C 语言结构体 —— 数据封装的利器
C语言结构体是一种用户自定义的数据类型,用于将不同类型的数据组合在一起,形成一个整体。它支持数据封装,便于管理和传递复杂数据,是程序设计中的重要工具。
|
29天前
|
存储 C语言
C语言如何使用结构体和指针来操作动态分配的内存
在C语言中,通过定义结构体并使用指向该结构体的指针,可以对动态分配的内存进行操作。首先利用 `malloc` 或 `calloc` 分配内存,然后通过指针访问和修改结构体成员,最后用 `free` 释放内存,实现资源的有效管理。
101 13
|
29天前
|
存储 编译器 数据处理
C 语言结构体与位域:高效数据组织与内存优化
C语言中的结构体与位域是实现高效数据组织和内存优化的重要工具。结构体允许将不同类型的数据组合成一个整体,而位域则进一步允许对结构体成员的位进行精细控制,以节省内存空间。两者结合使用,可在嵌入式系统等资源受限环境中发挥巨大作用。
57 11
|
23天前
|
存储 算法 程序员
C 语言指针详解 —— 内存操控的魔法棒
《C 语言指针详解》深入浅出地讲解了指针的概念、使用方法及其在内存操作中的重要作用,被誉为程序员手中的“内存操控魔法棒”。本书适合C语言初学者及希望深化理解指针机制的开发者阅读。
|
29天前
|
存储 C语言 开发者
C 语言指针与内存管理
C语言中的指针与内存管理是编程的核心概念。指针用于存储变量的内存地址,实现数据的间接访问和操作;内存管理涉及动态分配(如malloc、free函数)和释放内存,确保程序高效运行并避免内存泄漏。掌握这两者对于编写高质量的C语言程序至关重要。
52 11
|
21天前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
49 1