【C语言进阶】深度剖析数据在内存中的存储

简介: 【C语言进阶】深度剖析数据在内存中的存储

前言

到现在,我们都大概认识了C语言的基本内置类型有那些,那么这些类型到底是以什么样的形式存储到内存中的呢?

接下来我们就为大家揭晓这个谜团,原来整型和浮点型的存储并不一样。


一、数据类型介绍

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

//C语言有没有字符串类型?

//C语言是没有特别设置类似于Java的String字符串类型,但是我们可以通过char【】数组来实现对于字符串的存储。

   类型的意义:

1.使用这个类型开辟内存空间的大小(大小决定了使用范围)

2.如何看待内存空间的视角        ---        比如涉及到算术转换的时候,整型提升的时候,或者强制指针类型转换的时候,都要考虑类型的内存大小

1.1类型的基本归类

整型:       unsigned  表示无符号        

                 signed      表示有符号    且signed int  和  int   在大部分的编译器中本质上没有什么区别

char        字符型        1个字节

       unsigned char

       signed char

short        短整型        2个字节

       unsigned short

       signed short

int             整型            4个字节              

       unsigned int

       signed int

long           长整型        4个字节

       unsigned long

       signed long

浮点型:

float               单精度浮点数        4个字节

double           双精度浮点数        8个字节

构造类型:

       所谓的构造类型就是,这些类型是你可以自定义类型,不是C语言基本内置类型(整型家族、浮点型家族),可以自己设定类型名称,如结构体类型 struct arr{};struct arr就是这个结构体的类型名,当然也可以使用typedef来再次定义,就不多讲了。

       数组如 int arr[11]         数组类型为:int [11]  ,数组名为:arr  所以更改[ ]的数值就是不一样的数组类型

1.数组类型          如 int arr [ ]        其数组类型为    int [ ]       数组名为  arr          

2.结构体类型           struct  

3.枚举类型               enum

4.联合类型               union

指针类型:        

      以下类型的指针是样例,这些指针的类型 分别为 int*  char* float*  void*一般存放的是对应类型的地址,当然可以存放不同类型的地址(强制转换)这样可能会发生截断,下面会有样例

int *pi                整型指针pi
char *pc            字符型指针pc
float* pf             单精度浮点型指针pf
void* pv             无符号指针pv

空类型:

void        表示空类型 (无类型)

 通常是应用在函数的返回类型、函数的参数、指针类型

二、整型在内存中的存储

       上文介绍,一个变量在创建的时候,会在内存中开辟空间的,空间的大小是根据不同的类型而决定的。

接下来我们来认识一下整型家族是如何在内存中存储。

整形的范围在limits.h头文件中可以看到

2.1原码、反码、补码

初步认识:

1.  计算机中的整数有三种2进制的表示方法,即原码、反码、补码。

2. 三种方法都有符号位和数值位两部分,符号位用  ’ 0 ‘ 或 ’ 1 ‘来表示,0 表示为正数,1 表示位负数  数值位正数的原码、反码、补码都相同。负数的三种表示方法各不相同

原码:

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

反码:

将原码的符号位不变,数值位按位取反,0 -> 1 或 1 -> 0 ,得到反码

补码:

将反码加一得到补码

注意:

正数的三码合一(都相同),负数三码表示都不相同,按照上面的方法改变

2.2 认识数值在内存中的存储

对于整型来说,数值在内存中的存储是存放的补码

这个时候就有些同学开始疑问了,为什么三码中选择了补码?

这是因为:

1.在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和     数值域统一处理;
2.同时,加法和减法也可以统一处理(CPU只有加法器)此外,补码与原码相互转换,其运     算过程是相同的,不需要额外的硬件电路。

让我们看看vs中的内存存储形式:

我们让它显示的是十六进制,所以我们可以知道,0a 其实就是10 ,14实际上就是20(16+4)

b=20的二进制为

00000000000000000000000000010100

0000  0000  0000  0000  0000  0000  0001  0100   (4个4个分开)

  0        0        0        0        0        0        1        4   (16进制)

二进制是这样的,但是在内存中确实倒置存放的,这是为什么呢?

2.3 认识大小端

什么是大端小端:

大端(存储)模式,是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中;
小端(存储)模式,是指数据的低位保存在内存的低地址中,而数据的高位,,保存在内存的高地址中。

为什么要有大小端呢?

1.数据的存放,本质上任意顺序存储都可以,只要能存进去就可以,但是,存进去就要取出来。肆意顺序存放,等取出来的时候,就很麻烦,所以诞生了大小端这两种存放的方式

2.大端是正序排放,低位放在高地址,00 00 00 14 (二进制)放在大端为 00 00 00 14

3.小端是逆序排放,低位放在低地址,00 00 00 14 (二进制)放在小端为 14 00 00 00

4.正序存放,就正序取出,逆序存放,就逆序取出

请看一道面试题:

请简述大端字节序和小端字节序的概念,设计一个小程序来判断当前机器的字节序。

(以下两种方法都可以)

2.4 练习

答案是:-1         -1         255

关于char的小技巧:

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

我们已经介绍了整数家族在内存中的存储,接下来让我们看看,浮点型的存储是否有不同?

常见浮点数

1.23

1e10

浮点数家族包括,float、double、long double类型

浮点数的表示范围在float.h头文件中定义

3.1举例

为什么会这样呢?

这是因为浮点数的存储方式和整数存储方式不一样导致的

3.2浮点数的存储规则

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

(-1)^S * M * 2^E
(-1)^S表示符号位,当S=0,V为正数;当S=1,V为负数。
M表示有效数字,大于等于1,小于2。
2^E表示指数位

也就是说

对于32位的浮点数有这样存储空间的分配

对于64位浮点数

特别规定:

前面说过, 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。

将E从内存中取出还要分三种情况:

1.E不全为0或不全为1

这时,浮点数就采用下面的规则表示,即指数E的计算值减去127(或1023),得到真实值,再将有效数字M前加上第一位的1。

比如:

0.5(1/2)的二进制形式为0.1,由于规定正数部分必须为1,即将小数点右移1位,则为

1.0*2^(-1),其阶码为-1+127=126,表示为

01111110,而尾数1.0去掉整数部分为0,补齐0到23位00000000000000000000000,则其二进制表示形式为:

0 01111110 00000000000000000000000

2.E全为0

这时,浮点数的指数E等于1-127(或者1-1023)即为真实值,

有效数字M不再加上第一位的1,而是还原为0.xxxxxx的小数。这样做是为了表示±0,以及接近于0的很小的数字

3.E全为1

这时,如果有效数字M全为0,表示±无穷大(正负取决于符号位s)

如11111111  ---> 255   E=255-127  ,次方很高,正负无穷只看S为0还是1

3.3 例题的解释

       


总结

1.我们知道了整数和浮点数的在内存中的存储方式是不一样的,整数是通过原码、反码、补码二进制的形式,存储在内存中。

2.大小端的问题,在探寻整数的存储方式的时候,发现了大小端,这是为了方便数据的存取设定的两种方法,大端->低位存高地址  , 小端->低位存低地址。如何查询当前编译器是大小端的方法(面试题),vs默认是小端。

3.浮点数的SME存储,S表示正负(1bit),E表示几次方(unsigned int)(8bit或11bit),需要真实值加上127或1023(相应的返回的时候减去),M表示小数的存储(精度有23bit或者52bit)

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