数据在内存中的存储(了解大小端字节序浮点数在内存中存储)详细~

news2024/9/22 3:40:27

目录

1、整数在内存中的存储

2、了解大小端字节序

2.0 为什么有大小端之分呢?

3、练习题

3.1 练习01

3.2 练习02

3.3 练习03

3.4 练习04

3.5 练习05

3.6 练习06

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

4.0 浮点数在计算机内部的表示方法

4.1 浮点数存的过程

4.2 浮点数取的过程

4.3 回到开始时示例的代码,分析:


1、整数在内存中的存储

整数的2进制表示方法有三种,即原码、反码和补码

有符号的整数,三种表示方法均有符号位数值位两部分,符号位都是用0表示“正”,用1表示"负",最高位的一位是被当做符号位,剩余的都是数值位。

正整数的原、反、补码都相同。

负整数的三种表示方法各不相同。

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

反码:将原码的符号位不变,其他位依次按位取反得到反码。

补码:反码+1就得到补码。

对于整型来说:数据存放内存中其实存放的是补码。

原因:使用补码,可以将符号位和数值域统一处理;

同时,加法和减法也可以统一处理(CPU只有加法器)此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。

2、了解大小端字节序

在计算机科学中,字节序或端序是指多字节数据在内存中的存储顺序。主要有两种字节序:大端字节序(Big-Endian)小端字节序(Little-Endian)

大端(存储)模式: 是指数据的低位字节(LSB)内容保存在内存的高地址处,而数据的高位字节(MSB)内容,保存在内存的低地址处。

小端(存储)模式: 是指数据的低位字节(LSB)内容保存在内存的低地址处,而数据的高位字节(MSB)内容,保存在内存的高地址处。

✅示例: 

#include <stdio.h>
int main()
{
	int a = 0x11223344;

	return 0;
}

调试的时候,我们可以看到在a中的 0x11223344 这个数字是按照字节为单位,倒着存储的。

2.0 为什么有大小端之分呢?

产生原因:

在计算机系统中,我们是以字节为单位的,每个地址单元都对应着一个字节(8位)。然而,在C语言等编程语言中,除了8位的char类型外,还有16位的short型、32位的long型等数据类型。对于位数大于8位的处理器(如16位、32位或64位处理器),由于寄存器的宽度大于一个字节,因此必须解决如何将多个字节安排到内存中的问题。这就导致了大端模式和小端模式的产生。

应用场景:

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

1、硬件架构:不同的硬件架构可能采用不同的字节序方式。例如,Intel x86系列处理器采用的是小端模式,而MIPS、PowerPC等处理器则采用的是大端模式
2、文件格式:在文件格式中,常常需要使用特定的字节序来表示数据。例如,BMP图像文件中,像素数据通常采用小端模式存储;而WAV音频文件中,样本数据则采用大端模式存储。
3、网络传输:在网络传输数据时,通常需要将数据转换成网络字节序(即大端模式),以确保在不同机器之间的传输中不会出现问题。因此,大多数协议规定了网络字节序应该采用大端模式
4、数据库存储:在数据库中,常常需要对多字节数据类型进行排序和比较。由于不同的字节序方式会影响排序结果,因此在数据库设计中需要考虑字节序问题。

3、练习题

3.1 练习01

设计一个小程序来判断当前机器的字节序。

#include<stdio.h>

// 设计一个小程序来判断当前机器的字节序

int main()
{
	int num = 1;
	if (*(char*)&num == 1) // int*
	{
		printf("小端\n");
	}
	else
	{
		printf("大端\n");
	}
	return 0;
}

在VS2022 X86下运行结果:

函数写法:

#include<stdio.h>

// 函数写法
//int check_sys() // 第一种
//{
//	int num = 1;
//	if (*(char*)&num == 1)
//		return 1; // 小端
//	else
//		return 0; // 大端
//}
// 
int check_sys() // 第二种
{
	int num = 1;
	return *(char*)&num; // 返回1是小端返回0是大端
}

int main()
{
	if (check_sys() == 1)
	{
		printf("小端\n");
	}
	else
	{
		printf("大端\n");
	}
	
}

3.2 练习02

代码:

#include <stdio.h>
int main()
{
	char a = -1;
	signed char b = -1;
	unsigned char c = -1;
	printf("a=%d,b=%d,c=%d\n", a, b, c); // a=-1,b=-1,c=255 
	return 0;
}

解析:

%d —— 打印有符号的整数,%u 打印的是无符号的整数。其中a是有符号的a, b也是有符号的,打印结果一样,而unsigned char 则是无符号的,所以我们先找出-1的补码,而c存储在char时存8位(即11111111),但要以%d的形式打印,需要整型提升,c是unsigned char,无符号位的,高位数补0,结果为(00000000 00000000 00000000 11111111),再以%d打印,打印的是有符号的数 ,符号位又为0,为正数,原反补相同。所以内存存的为这个(00000000 00000000 00000000 11111111)补码的话,原码也一样为(00000000 00000000 00000000 11111111),所以结果为正的255。

运行结果:

char 是有符号还是无符号的这个不太确定,取决于编译器~但是大部分编译器上char==signed char

3.3 练习03

#include <stdio.h>
int main()
{
 char a = -128;
 printf("%u\n",a);
 return 0;
}

解析:

%u —— 打印的是无符号的整数。在%u 的角度,它认为在内存中存储的是无符号的整数。

-128 原码为(10000000000000000000000010000000),求出补码(11111111111111111111111110000000 ),char a 只能存储8位(即10000000)要打印%u的无符号整数,所以要整型提升,char为有符号char ,高位补1,补符号位。结果为(11111111111111111111111110000000)提升完,当作是内存内的补码,%u 打印的是无符号的整数,故原反补相同,所以原码也是(11111111111111111111111110000000),结果用计算机算出为:(4294967168)

改编一下代码:

#include <stdio.h>
int main()
{
	char a = 128;
	printf("%u\n", a);//  // 4294967168
	return 0;
}

结果一样的:思路跟上面差不多,就不多解析了。

实际上char 类型的取值范围(signed char)为-128~127 ,当你给128 时根本存不下,相当于内存放的就是-128。所以结果一样。如果是无符号char( unsigned char )取值范围是0~255。第一位就不是符号位了。

扩展:( signed short)类似的。

格式决定了我如何看待数据,有时候跟它的类型没太大关系。

如下面的代码,结果是一样的,不建议这样写代码,最好是signed int 就用%d 打印,是unsigned int 就用%u 打印

3.4 练习04

代码:

#include <stdio.h>
#include<string.h>
int main()
{
	char a[1000];
	int i;
	for (i = 0; i < 1000; i++)
	{
		a[i] = -1 - i;
	}
	printf("%zd", strlen(a));// 255
	return 0;
}

strlen 求得是字符串的长度,统计的是 \0 之前出现的字符个数,我们要找到数组中 0 的位置,如图所示,已知char 的范围为-128~127。

3.5 练习05

代码:

循环条件恒成立(恒为真),代码死循环。

#include <stdio.h>
unsigned char i = 0; // 全局变量
// 0~255
int main()
{
	for (i = 0; i <= 255; i++)
	{
		printf("hello world\n"); // 死循环打印hello world
	}
	return 0;
}

unsigned char ,无符号整型最小值为0,循环条件不可能小于0,恒成立,死循环。 

#include <stdio.h>
#include<windows.h>
int main()
{
	unsigned int i;
	for (i = 9; i >= 0; i--)
	{
		printf("%u\n", i);// 死循环
		Sleep(100);// 休眠查看
	}
	return 0;
}

3.6 练习06

代码:

#include <stdio.h>

//X86环境 ⼩端字节序

int main()
{
	int a[4] = { 1, 2, 3, 4 };
	int* ptr1 = (int*)(&a + 1);
	int* ptr2 = (int*)((int)a + 1);
	printf("%x,  %x", ptr1[-1], *ptr2);// 4,  2000000
    // // %x 是十六进制打印
	return 0;
}

解析:

&a 则是取出了整个数组的地址,&a+1跳过了整个数组,赋给ptr1 强制类型转换,ptr1 指向同个位置,ptr1[-1]---> *(ptr1-1),整型指针解应用得到 4

a 为数组名,就是首元素的地址,强转为int ,把它当作整数,(int)a + 1为整数+1,其实只是向后偏移一个字节而已。*(ptr2),整型指针解引用,访问四个字节 00 00 00 02,在内存中它是小端存放,要还原成真实值为 0x02 00 00 00,前面的0x0不打印,所以结果为2000000

 

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

常见的浮点数:3.14159、1E10等,浮点数家族包括: float、double、long double 类型。 浮点数表示的范围: float.h 中定义

✅示例:

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

运行结果:

观察得到:如果我以整数的形式放进去,以浮点数取出,结果不对,反之,我以浮点数的形式放进去,以整数取出结果也不对。但是以整数(浮点数)的形式放进去,以整数(浮点数)取出,结果对。下面的例子说明了整数和浮点数在内存中存储方式有区别。

4.0 浮点数在计算机内部的表示方法

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

V   =  (−1)^s ∗ M ∗ 2^E

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

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

2^E 表示指数位

10进制转为2进制简单示例:

二进制中,其实就是把底数的10变成了2,如在10进制中,1.011x10^2=101.1,而在2进制中,把底数10变成了2,1.011x2^2=101.1

IEEE 754规定:

对于32位的浮点数,最高的1位存储符号位S,接着的8位存储指数E,剩下的23位存储有效数字M

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

4.1 浮点数存的过程

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位(即1加上尾数位的23位)有效数字。

至于指数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。

     保存为64位浮点数的时候,E保存为10+1023=1033,即10000001001。

4.2 浮点数取的过程

指数E从内存中取出还可以再分成三种情况

① E不全为0或不全为1

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

比如:0.5 的二进制形式为0.1,由于规定正数部分必须为1,即将小数点右移1位,则1.0*2^(-1),其阶码(指数位)为-1+127(中间值)=126,表示为01111110,而尾数1.0去掉整数部分为0,补齐0到23位 00000000000000000000000,则其二进制表示形式为:

0 01111110 00000000000000000000000

这里S为0,E为01111110,M为00000000000000000000000

又比如:

 下面这两种情况比较少见:

② E全为0

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

③ E全为1

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

4.3 回到开始时示例的代码,分析:

为什么 6 还原成浮点数,就成了 0.000000 ? 6以整型的形式存储在内存中,得到如下二进制序列:

0 00000000 00000000000000000000110

首先,将 6 的二进制序列按照浮点数的形式拆分,得到第一位符号位S = 0,后面8位的指数 E=00000000 , 最后23位的有效数字M = 00000000000000000000110。由于指数E全为0,符合E为全0的情况。因此,浮点数V就写成:

V=0.00000000000000000000110 * 2^(-126)= 1.10*2^(-147)

显然,V是一个很小的接近于0的正数,所以用十进制小数表示就是0.000000。

浮点数6.0,为什么整数打印是1086324736呢?

浮点数6.0的二进制0110.1 换算成科学技术法:1.100*2^2

故:6.0=(-1)^0 * 1.100 * 2^2

所以第一位的符号位S=0,有效数字M等于100后⾯再加20个0,凑满23位,指数E等于2+127=129, 即010000001,写成二进制形式,S+E+M:

 0 10000001 100 0000 0000 0000 0000 0000

这个32位的二进制数,被当做整数来解析的时候,就是整数在内存中的补码,原码也一样,所以最终结果为 1086324736 。

喜欢的话

⛳ 点赞☀收藏 ⭐ 关注!

如有不足欢迎评论区指出~

Respect!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2043459.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

FreeRTOS——优化系统(基于百问网FreeRTOS教学视频)

文章目录 一、精细调整栈大小二、打印所有任务的栈信息三、统计CPU占比找出有问题的任务 一、精细调整栈大小 在创建任务时分配了栈&#xff0c;可以填入固定的数值比如 0xa5&#xff0c;以后可以使用以下函数查看" 栈的高水位"&#xff0c;也就是还有多少空余的栈空…

完美解决RTX5源码工程+最新emWin6.40的编译兼容问题,使能C编译器使用C11可解决

最新的emWin6.40仅提供了.a格式库&#xff0c;这个库兼容MDK&#xff0c;IAR和GCC&#xff0c;但是在MDK AC6下使用需要做如下操作 -fno-short-wchar -fshort-enums 他这个操作&#xff0c;正好跟RTX5源码工程添加的一个设置冲突了&#xff0c;通过搜索资料&#xff0c;发现使…

性能测试 —— linux服务器搭建JMeter+Grafana+Influxdb监控可视化平台!

前言 在当前激烈的市场竞争中&#xff0c;创新和效率成为企业发展的核心要素之一。在这种背景下&#xff0c;如何保证产品和服务的稳定性、可靠性以及高效性就显得尤为重要。 而在软件开发过程中&#xff0c;性能测试是一项不可或缺的环节&#xff0c;它可以有效的评估一个系…

优先级队列的实现

什么是优先级队列 优先级队列是一种特殊的数据结构&#xff0c;它类似于队列或栈&#xff0c;但是每个元素都关联有一个优先级或权重。在优先级队列中&#xff0c;元素的出队顺序不是简单地按照它们进入队列的先后顺序&#xff08;先进先出&#xff0c;FIFO&#xff09;&#…

vulnhub靶机 DC-9(渗透测试详解)

一、靶机信息收集 1、靶机下载 https://download.vulnhub.com/dc/DC-9.zip 2、靶机IP扫描 3、探测靶机主机、端口、服务版本信息 4、靶机目录扫描 二、web渗透测试 1、访问靶机IP 查看页面功能点&#xff0c;发现一个搜索框和登录框 2、测试一下是否存在sql注入 查看当前数…

快速解析数据挖掘,最短时间明白什么是数据挖掘------下

信息损失函数 &#xff08;Information Loss Function&#xff09;是衡量在数据转换或处理过程中信息丢失的程度的函数。在数据科学、机器学习和统计学中&#xff0c;信息损失是一个重要的概念&#xff0c;尤其是在数据降维、特征选择、数据压缩和隐私保护等领域。 信息损失函…

解密!抖音百万粉丝博主三维地图视频都用到了什么GIS数据和技术

引言 在抖音上有许多诸如三维地图科普局、三维地图看世界和三维地图鉴赏等百万粉丝博主靠着三维地图科普城市、景区、人文和地理视频获赞百万&#xff0c;在我们浏览视频时犹如身临其境一般&#xff0c;那么制作这些视频需要什么GIS技术呢&#xff1f;如何利用MapMost技术自己…

怎么防止源代码泄露?十种方法杜绝源代码泄密风险

源代码是软件开发的核心资产之一&#xff0c;保护其不被泄露对企业的安全至关重要。源代码泄露不仅可能导致知识产权的丧失&#xff0c;还可能给企业带来经济损失和品牌形象的损害。以下是十种有效的方法&#xff0c;可以帮助企业杜绝源代码泄密的风险。 1. 代码加密 对源代码…

LeetCode 3148.矩阵中的最大得分:每个元素与其左或上元素之差的最大值(原地修改O(1)空间)

【LetMeFly】3148.矩阵中的最大得分&#xff1a;每个元素与其左或上元素之差的最大值&#xff08;原地修改O(1)空间&#xff09; 力扣题目链接&#xff1a;https://leetcode.cn/problems/maximum-difference-score-in-a-grid/ 给你一个由 正整数 组成、大小为 m x n 的矩阵 g…

ESVC论文笔记

ESVC: COMBINING ADAPTIVE STYLE FUSION AND MULTI-LEVEL FEATURE DISENTANGLEMENT FOR EXPRESSIVE SINGING VOICE CONVERSION阅读笔记 发现问题 虽然SVC在自然度和相似度方面都取得了很好的效果,但音频中除了歌手身份之外,情感表达也是传递歌手感情和态度的必要条件&#xf…

17.实现一个算法根据电话按键上的数字和字母的映射关系,输入一个或多个数字返回所有它能表示的全部字母组合

17. Letter Combinations of a Phone Number 题目 Given a string containing digits from 2-9 inclusive, return all possible letter combinations that the number could represent. A mapping of digit to letters (just like on the telephone buttons) is given belo…

电子音乐制作软件有哪些 电音制作用什么软件 好用的能够创作音乐的软件推荐 电音基础新手入门

电子音乐目前已经成为了年轻人的一大爱好&#xff0c;而全国各地随处可见的音乐节更是代表着电子音乐文化已经逐渐被年轻人所接受&#xff0c;在这样的大背景下&#xff0c;一些年轻人也开始了自己创作电子音乐的道路。现在有了软件的帮助&#xff0c;我们能够更加随心所欲的创…

卡码网KamaCoder 110. 字符串接龙

题目来源&#xff1a;110. 字符串接龙 C题解1&#xff1a;深度搜索。一条路径走到黑&#xff0c;并且记录到达当前节点的最短路径长度&#xff0c;不断更新&#xff0c;如果不是最短路径就不用再遍历那条路。 #include <iostream> #include <vector> #include <…

数据结构基本概念和术语

概论 1.1 基本概念和术语 1.1.1 基本概念 计算机处理的的是数值性数据&#xff0c;当计算机处理用户信息表中的数据的时候&#xff0c;需要弄清3个问题 1.数据的逻辑结构 数据之间存在怎样的内在联系&#xff0c;数据中&#xff0c;有且只有一个是首节点/尾结点&#xff0…

Squish 8.0现已发布

本文翻译自&#xff1a;Squish 8.0 Available Now 原文作者&#xff1a;Qt Group质量保证高级解决方案工程师Katarina Behrens 审校&#xff1a;Jinjing Li Squish团队非常激动地宣布Squish GUI Tester 8.0现已发布。对于自动化跨平台GUI测试而言&#xff0c;这是一款软件质量…

41-设计规则:线宽规则

1.设置电源线规则和信号线规则 2.设置信号线规则 3.设置电源线规则 如果未生效&#xff1a; ① 提升优先级即可。 ②查看使能选项有没有勾选

2024医疗器械网络交易服务第三方平台备案申请流程

前几天&#xff0c;小编给大家分享了药品网络交易第三方平台备案申请流程&#xff0c;好多客户就来问&#xff0c;那医疗器械网络交易服务第三方平台备案怎么办理呢&#xff1f; 今天&#xff0c;就给大家好好聊聊医疗器械网络交易服务第三方平台备案申请流程&#xff0c;供大…

Xilinx课程,就这么水灵灵地上线了~

如果你想了解&#xff1a; 如何利用精通流水线&#xff08;Pipeline&#xff09;技术&#xff0c;让电路设计效率倍增&#xff1f; 如何掌握利用性能基线指导设计流程的方法&#xff1f; 如何理解集成电路设计中的UltraFast Design Methodology Implementation设计方法学中的…

C++拾趣——编译器预处理宏__COUNTER__的应用场景

大纲 生成唯一标识符调试信息宏展开模板元编程代码 在C中&#xff0c;__COUNTER__是一个特殊的预处理宏&#xff0c;它主要被用来生成唯一的整数标识符。这个宏是由一些编译器&#xff08;如GCC和Visual Studio&#xff09;内置支持的&#xff0c;而不是C标准的一部分。它的主要…

《AI视频类工具之五——​ 开拍》

一.简介 官网:开拍 - 用AI制作口播视频用AI制作口播视频https://www.kaipai.com/home?ref=ai-bot.cn 开拍是一款由美图公司在2023年推出,利用AI技术制作的短视频分享应用。这款工具通过AI赋能,为用户提供了从文案创作、视频拍摄到视频剪辑、包装的一站式解决方案,极大地…