C语言详细分析不同类型数据在内存中的存储

 更新时间:2022年8月18日 19:20  点击:57 作者:悲伤猪小猪

数据类型的介绍

在我们之前的学习当中我们已经介绍了基本的内置类型

char 字符数据类型

short 短整型

int 整形

long 长整型

long long 更长的整形

float 单精度浮点数

double 双精度浮点数

这些类型的意义是:

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

2.如何看待内存空间的视角。

类型的基本归类

整形

整形中分为有符号整形和无符号整形,因为我们生后中有些数值需要有正数和负数之分,例如温度我们就可以使用有符号整形,但是有些不需要负数的数值例如身高,我们就可以使用无符号整形。

char

unsigned char

signed char

short

unsigned short [int]

signed short [int]

int

unsigned int

signed int

long

unsigned long [int]

signed long [int]

浮点型

float

double

构造类型

数组类型

结构体类型 struct

枚举类型 enum

联合类型 union

指针类型

int pi;

char pc;

float pf;

void pv;

空类型

void表示空类型(无类型)

通常应用于函数的返回类型,函数的参数、指针类型

整形在内存中的存储

我们都知道,创建一个变量需要在内存中开辟空间,那变量究竟是怎么在内存当中存储的呢。我们又要提到原码、反码、补码的概念了。

在计算机中整数有三种表示方式即原码、反码、补码,他们都有符号位和数值位,符号位用0表示正数,用1表示负数。

正整数的原码、反码、补码相同,都是直接将属猪按照正负数的形式翻译成二进制形式就可以得到原码。

负整数的原码、反码、补码

原码

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

反码

将原码的符号位不变,其他位一次按位取反就可以得到反码

补码

反码+1就是补码

对于一个整形,数据在内存当中存放的其实是他的补码。那么为什么是补码而是不是原码呢,原因是使用补码,可以将符号位和数值域统一处理,同时加法和减法也可以统一处理,我们的cpu只有加法器,此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。

例如:

1 - 1
因为cpu中只有加法器,所以我们需要将减法转化为加法,1+ (-1)如果内存中存储的是原码,那么他们相加的结果为
00000000 00000000 00000000 00000001 1的原码
10000000 00000000 00000000 00000001 -1的原码
10000000 00000000 00000000 00000010 相加为-2
我们运算的答案是错误的,但是当我们存储的是补码时:
00000000 00000000 00000000 00000001 1的补码
11111111 11111111 11111111 11111111 -1的补码
00000000 00000000 00000000 00000000 相加0
使用补码运算时,得到正确的答案

int main()
{
	int a = 20;
	int b = -10;
	return 0;
}

如上图编译器为了方便显示的是16进制数,其实对于一个整形,数据在内存当中存放的还是他的补码,我们可以是尝试还原一下。

例如**-10**
原码:10000000 00000000 00000000 00001010
反码:11111111 11111111 11111111 11110101
补码:11111111 11111111 11111111 11110110
将他的补码转换成16进制就变成了ff ff ff f6,跟我们通过调试看到的内容一样

但是我们通过对比发现虽然存储的是补码,但是存储的顺序好像不一样,这是怎么回事呢?

大小端介绍

当我们的数据大于一个字节时,数据的存储就有了顺序问题。例如:一个十六进制数0x11223344,我们说11为数据的高位,44为数据的低位。

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

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

int main()
{
	int a = 0x11223344;
	return 0;
}

我们通过调试得出,在vs2019中为小端存储模式,因为数据的低位44存储在地址处,高位11存储在高地址处。

一道笔试题

我们通过一道笔试题来巩固一下我们刚才所总结的知识。题目:请简述大端字节序和小端字节序的概念,设计一个小程序来判断当前机器的字节序。

小端字节序存储

把数据的低位存储在内存的低地址处,高位字节的内容,存储在内存的高地址处 大端字节序存储 把数据的低字节的内容,存放到内存的高地址处,高字节内容,存放在低地址处

int check_sys()
{
	int a = 1;
	return *(char*)&a;
}
int main()
{
	if (check_sys() == 1)
	{
		printf("小端存储\n");
	}
	else
	{
		printf("大端存储\n");
	}
	return 0;
}

浮点数在内存中的存储

首先我们通过一段代码来观察浮点数在内存中的存储

int main()
{
	int n = 9;
	float* pFloat = (float*)&n;
	printf("n的值为:%d\n", n);
	printf("*pFloat的值为:%f\n", *pFloat);
	*pFloat = 9.0;
	printf("num的值为:%d\n", n);
	printf("*pFloat的值为:%f\n", *pFloat);
	return 0;
}

以我们的认识,使用%d打印应该打印9使用%f打印应该打印9.0,但事实是这样的嘛

我们将代码跑起来发现,并非我们所认识的,那这说明了什么,说明了浮点型与整形在内存当中的存储规则是不一样的,那我们先了解一下浮点数究竟怎么储存的。

浮点数存储规则

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

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

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

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

2^E表示指数位。

例如:十进制5.0,写成二进制是101.0,相当于1.0122

所以s = 0,m = 1.01, 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。

既然我们可以将浮点数存储,我们就可以将他们取出来,但是指数E从内存中取出分为三种情况:

E不全为0或不全为1

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

E全为0

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

E全为1

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

剖析题目

这些就是浮点数在内存当中的存储规则,了解了这些后让我们回到最开始的那道题,我们进行分析。

首先我们创建了一个整形变量,那么他在内存中存储的是什么呢?是他的补码00000000000000000000000000001001,当我们将他看作一个浮点数打印时,我们可以得到S = 0,M = 000 0000 0000 0000 0000 1001,E = 00000000。所以经过计算(-1)^ 0 × 0.00000000000000000001001×2 ^ (-126) = 1.001×2 ^ (-146), 所以根据规则他是一个非常接近0的数,使用浮点数打印就是0.000000

笔试题的第二部分,我们将一个浮点型使用整形打印,同样的道理,我们先将9.0用浮点数的方式存进内存当中,9.0写成二进制为1001.0,可以写成(-1)^0 * 1.001*2^3,我们可以推出二进制形式:0 10000010 001 0000 0000 0000 0000 0000,所以将他作为一个整形打印时就会是一个非常大的数为1091567616

到此这篇关于C语言详细分析不同类型数据在内存中的存储的文章就介绍到这了,更多相关C语言数据存储内容请搜索猪先飞以前的文章或继续浏览下面的相关文章希望大家以后多多支持猪先飞!

原文出处:https://blog.csdn.net/weixin_64182409/article/details/126312

相关文章

  • C语言实现放烟花的程序

    这篇文章主要为大家详细介绍了C语言实现放烟花的程序,有音乐播放,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-02-23
  • php简单数据操作的实例

    最基础的对数据的增加删除修改操作实例,菜鸟们收了吧...2013-09-26
  • php把读取xml 文档并转换成json数据代码

    在php中解析xml文档用专门的函数domdocument来处理,把json在php中也有相关的处理函数,我们要把数据xml 数据存到一个数据再用json_encode直接换成json数据就OK了。...2016-11-25
  • 解决Mybatis 大数据量的批量insert问题

    这篇文章主要介绍了解决Mybatis 大数据量的批量insert问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-01-09
  • 详解如何将c语言文件打包成exe可执行程序

    这篇文章主要介绍了详解如何将c语言文件打包成exe可执行程序,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-25
  • mybatis-plus 处理大数据插入太慢的解决

    这篇文章主要介绍了mybatis-plus 处理大数据插入太慢的解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-12-18
  • Antd-vue Table组件添加Click事件,实现点击某行数据教程

    这篇文章主要介绍了Antd-vue Table组件添加Click事件,实现点击某行数据教程,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-11-17
  • C#连接SQL数据库和查询数据功能的操作技巧

    本文给大家分享C#连接SQL数据库和查询数据功能的操作技巧,本文通过图文并茂的形式给大家介绍的非常详细,需要的朋友参考下吧...2021-05-17
  • 解决使用OpenCV中的imread()内存报错问题

    这篇文章主要介绍了解决使用OpenCV中的imread()内存报错问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-03-16
  • 详解如何清理redis集群的所有数据

    这篇文章主要介绍了详解如何清理redis集群的所有数据,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-18
  • 解决tensorflow训练时内存持续增加并占满的问题

    今天小编就为大家分享一篇解决tensorflow训练时内存持续增加并占满的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-04-22
  • 分享MYSQL插入数据时忽略重复数据的方法

    使用下以两种方法时必须把字段设为”主键(PRIMARY KEY”或”唯一约束(UNIQUE)”。1:使用REPLACE INTO (此种方法是利用替换的方法,有点似类于先删除再插入) 复制代码 代码如下:REPLACE INTO Syntax REPLACE [LOW_PRIO...2013-10-04
  • 详解分析MySQL8.0的内存消耗

    这篇文章主要介绍了详解分析MySQL8.0的内存消耗,帮助大家更好的理解和学习使用MySQL,感兴趣的朋友可以了解下...2021-03-23
  • C语言中求和、计算平均值、方差和标准差的实例

    这篇文章主要介绍了C语言中求和、计算平均值、方差和标准差的实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-12-10
  • postgresql数据添加两个字段联合唯一的操作

    这篇文章主要介绍了postgresql数据添加两个字段联合唯一的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-02-04
  • 浅谈redis key值内存消耗以及性能影响

    这篇文章主要介绍了浅谈redis key值内存消耗以及性能影响,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-02-07
  • vue 获取到数据但却渲染不到页面上的解决方法

    这篇文章主要介绍了vue 获取到数据但却渲染不到页面上的解决方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-11-19
  • Mysql中存储UUID去除横线的方法

    参考:http://stackoverflow.com/questions/412341/how-should-i-store-guid-in-mysql-tables通常用UUID做唯一标识,需要在数据库中进行存储。UUID的格式 复制代码 代码如下: String string = UUID.randomUUID().toStrin...2015-03-15
  • perl从文件中读取数据并输出的实现代码

    perl从文件中读取数据并输出,附一个蛋白质序列的读取,有需要的朋友可以参考下...2020-06-29
  • 详解C语言中const关键字的用法

    这篇文章主要对C语言中const关键字的用法进行了详细的分析介绍,需要的朋友可以参考下...2020-04-25