C语言进阶--数据的存储

news2024/12/24 8:46:42

目录

数据类型介绍

基本内置类型:

类型的意义:

类型的基本归纳: 

整型在内存中的存储 

原码,反码和补码:

大小端存储模式:

大小端产生原因:

浮点型在内存中的存储


数据类型介绍

基本内置类型:

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

类型的意义:

  1. 使用这个类型开辟内存空间的大小(大小决定了使用范围);
  2. 如何看待内存空间的视角

类型的基本归纳: 

整型家族:

  1. char:unsigned char,signed char
  2. short:unsigned short [int],signed int
  3. int:unsigned int,signed int
  4. long:unsigned long [int],signed long [int]

浮点数家族:

  1. float
  2. double

构造类型:

  1. 数组类型
  2. 结构体类型 sturct
  3. 枚举类型 enum
  4. 联合类型 union

指针类型:

  1. int* pi
  2. char* pc
  3. float* pf
  4. void* pv

空类型: 

      void:表示空类型(无类型) ,通常应用于函数的返回类型,函数的参数,指针类型

整型在内存中的存储 

原码,反码和补码:

计算机中的有符号数有三种表示方式,即原码,反码和补码。三种表示方式均有符号位和数值位两部分,符号位都是用0表示“正”,用1表示“负”,而数值位三种表示方法各不相同。 

正数:原,反,补码都相同

负数:原,反,补码需要按照如下规则进行运算

  1. 原码:直接将数值按照正负数的形式翻译成二进制形式就可以得到原码
  2. 反码:将原码的符号位不变,其他位一次按位取反就可以得到反码
  3. 补码:反码+1

注意:对于整型来说,数据存放在内存中存放的其实是数据的补码

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

我们通过以下程序来查看数据在内存中的存储:

 我们发现数据在内存中确实是以16进制的补码形式进行存储的,但是我们发现数据的存放顺序是存在差异的,那这又是为何?

大小端存储模式:

何为大小端?

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

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

大小端产生原因:

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

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

//基础版
int ckeck_sys()
{
	int a = 1;//00 00 00 01
	char* p = (char*)&a;//强制类型转换,取第一个字节

	if (*p == 1)
	{
		return 1;//小端
	}
	else
	{
		return 0;//大端
	}

	return 0;
}

//进阶版
int check_sys()
{
	int a = 1;
	return *(char*)&a;
}


int main()
{
	int ret = check_sys();

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

运行结果:

案例二:下例程序输出什么?

int main()
{
	char a = -1;
	signed char b = -1;
	unsigned char c = -1;
	printf("a=%d, b=%d, c=%d",a, b, c);
	return 0;
}

分析:

int main()
{
	//char是signed char还是unsigned char,C语言并没有明确规定,取决于编译器,大多数编译器下是signed char

	char a = -1;
	//原:10000000 00000000 00000000 00000001
	//反:11111111 11111111 11111111 11111110
	//补:11111111 11111111 11111111 11111111
	//取8位:11111111

	signed char b = -1;
	//同a

	unsigned char c = -1;
	//原:10000000 00000000 00000000 00000001
	//反:11111111 11111111 11111111 11111110
	//补:11111111 11111111 11111111 11111111
	//取8位:11111111

	printf("a=%d,b=%d,c=%d\n",a,b,c);//-1,-1,255
	//%d是打印有符号数,%d是用来输出十进制的整数,对应的数据类型是 int
	//当我们打印a时,要发生整型提升,有符号数则直接补符号位,则11111111->11111111 11111111 11111111 11111111->转原码:10000000 00000000 00000000 00000001得-1
	//b同a一样
	//当我们打印c时,要发生整型提升,无符号数则直接补0,则11111111->00000000 00000000 00000000 11111111->转原码:00000000 00000000 00000000 11111111得255
	
	return 0;
}

运行结果:

案例三:下例程序输出什么?

int main()
{
	char a = -128;
	printf("%u\n", a);
	return 0;
}

分析:

int main()
{
	char a = -128;
	//原:10000000 00000000 00000000 10000000
	//反:11111111 11111111 11111111 01111111
	//补:11111111 11111111 11111111 10000000
	//取8位:10000000

	printf("%u\n",a);
	//%u是打印无符数,%u以无符号的十进制形式输出整数,对应的数据类型是unsigned int
	//当我们打印a时,要发生整型提升,有符号数则直接补符号位,则11111111 11111111 11111111 10000000,打印的是无符号数,所以原反补相同,所以直接将二进制转换成十进制输出:4294967168

	return 0;
}

运行结果:

案例四:下例程序输出什么?

int main()
{
	char a = 128;
	printf("%u\n", a);
	return 0;
}

分析:

int main()
{
	char a = 128;
	//原:00000000 00000000 00000000 10000000
	//反:00000000 00000000 00000000 10000000
	//补:00000000 00000000 00000000 10000000
	//取8位:10000000

	printf("%u\n", a);
	//%u打印的是无符号的整数,%u以无符号的十进制形式输出整数,对应的数据类型是unsigned int
	//当我们打印a时,要发生整型提升,有符号数则直接补符号位,则11111111 11111111 11111111 10000000,打印的是无符号数,所以原反补相同,所以直接将二进制转换成十进制输出:4294967168

	return 0;
}

运行结果:

案例五:下例程序输出什么?

int main() 
{
	int i = -20;
	unsigned int j = 10;
	printf("%d\n", i + j);
	return 0;
}

分析:

int main()
{
	int i = -20;
	//原:10000000 00000000 00000000 00010100
	//反:11111111 11111111 11111111 11101011
	//补:11111111 11111111 11111111 11101100

	unsigned int j = 10;
	//原:00000000 00000000 00000000 00001010
	//反:00000000 00000000 00000000 00001010
	//补:00000000 00000000 00000000 00001010

	printf("%d\n",i+j);
	//俩补码相加:
	//11111111 11111111 11111111 11101100
	//00000000 00000000 00000000 00001010
	//11111111 11111111 11111111 11110110:补
	//10000000 00000000 00000000 00001001:反
	//10000000 00000000 00000000 00001010:原->-10

	return 0;
}

运行结果:

案例六:下例程序输出什么?

int main()
{
	unsigned int i;
	for(i = 9; i >= 0; i--)
	{
		printf("%u\n", i);
	}
	return 0;
}

分析:

#include<windows.h>
int main()
{
	unsigned int i;
	for (i = 9; i >= 0; i--)
	{
		printf("%u\n",i);
		//9 8 7 6 5 4 3 2 1 0 (-1的补码:11111111 11111111 11111111 11111111对应的无符号数:4294967295) (-2的补码对应的无符号数:4294967294) 依次循环并进入死循环
		//i是个无符号类型的,也就是i>=0恒成立,所以这个循环的条件就恒成立

		Sleep(1000);//单位是毫秒
	}

	return 0;
}

运行结果:

案例七: 下例程序输出什么?

int main()
{
	char a[1000];
	int i;
	for(i = 0; i < 1000; i++)
	{
		a[i] = -1 - i;
	}
	printf("%d", strlen(a));
	return 0;
}

分析:

int main()
{
	char a[1000];

	int i;

	for (i = 0; i < 1000; i++)
	{
		a[i] = -1 - i;
	}
	printf("%d\n",strlen(a));//255
	//即-1 -2 -3...-128 127(-129对应的二进制截段) 126 125...1 0,strlen读取到0的时候则停止
	//-129的原码:10000000 00000000 00000000 10000001->反码:11111111 11111111 11111111 01111110->补码:11111111 11111111 11111111 01111111,取8位:01111111得127 
	//strlen是求字符串的长度,找的是\0的位置,统计的是\0之前出现了多少哥字符,注意'\0'的ASCII值是0

	return 0;
}

运行结果:

案例八:下例程序输出什么?

unsigned char i = 0;
int main()
{
	for (i = 0; i <= 255; i++)
	{
		printf("hello world\n");
	}

	return 0;
}

分析:

unsigned char i = 0;//0-255
int main()
{
	for (i = 0; i <= 255; i++)
	{
		printf("hello world\n");//死循环,00000000-11111111,+1得100000000,舍弃最高位1得00000000
	}

	return 0;
}

运行结果:死循环

char小结:

char虽然是字符类型,但是字符类型存储时,存储的是字符的ASCII码,而ASCII的值为整数;
char究竟是有符号数还是无符号数,这是不确定的,要取决于对应的编译器;
无符号char:0-255,有符号char:-128-127,-128的补码是:1000 0000
char的具体意义只取决于读取内存数据并解释的编译器,现在大多数编译都将char解释成signed char

                                                           

浮点型在内存中的存储

浮点数家族包括:float,double,long double类型,其表示的范围是由float.h定义

案例分析:

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;
}

运行结果:

num和*pFloat在内存中明明是同一个数,为什么浮点数和整数的解读结果会差这么大?要理解这个结果,一定要明白浮点数在计算机内部的表示方法。

整数和浮点数在内存中的存储方式是有差异的                                                                                                                                                      
任何一个二进制浮点数V都可以表示成:(-1)^S*M*2^E

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

举例:5.5->二进制表示:101.1->(-1)^0*1.011*2^2->S=0;M=1.011;E=2
            9.0->二进制表示:1001.0->(-1)^0*1.001*2^3->S=0;M=1.001;E=3

IEEE 754规定:

  1. 单精度浮点数存储模型:对于32位的浮点数,最高的1位是符号位S,接着的8位是指数E,剩下的23位为有效数字M
  2. 双精度浮点数存储模型:对于64位的浮点数,最高的1位是符号位S,接着的11位是指数E,剩下的52位为有效数字M 

对有效数字M:

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

案例一:

int main()
{
	float f = 5.5;
	//101.1
	//(-1)^0*1.011*2^2
	//S=0
	//E=2
	//M=1.011
	//存储到内存:
	//E+127=2+127=129
	//0 10000001 01100000000000000000000
	//S     E             M
	//0x40b00000

	return 0;
}

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

  1. E不全为0或不全为1:这时,浮点数就采用下面的规则表示,即指数E的计算值减去127或1023,得到真实值,再将有效数字M前加上第一位的1;
  2. E为全0:这时,浮点数的指数E等于1-127(或者1-1023)即为真实值,有效数字M不再加上第一位的1,而是还原为0.xxxxxx的小数。这样做是为了表示±0,以及接近于0的很小的数字;
  3. E全为1:这时,如果有效数字M全为0,表示±无穷大(正负取决于符号位s)

案例二:

int main()
{
	float f = 0.5;
	//0.1
	//(-1)^0*1.0*2^-1
	//S=0
	//M=1.0
	//E=-1
	//存储到内存:
	//E+127=-1+127=126
	//0 01111110 00000000000000000000000
	//S     E              M
	//0x3f000000

	return 0;
}

在了解完浮点数的存储方式之后,我们再来回顾之前的案例分析,

int main()
{
	int n = 9;
	//00000000 00000000 00000000 00001001
	//以浮点数的存储方式进行读取
	//0 00000000 00000000000000000001001
	//S    E              M
	//E为全0:浮点数的指数E等于1-127(或者1-1023)即为真实值,有效数字M不再加上第一位的1,而是还原为0.xxxxxx的小数
	//E=1-127=-126
	//M=0.00000000000000000001001
	//(-1)^0*0.00000000000000000001001*2^-126=1*0.00000000000000000001001*2^-126,是一个无限接近0的数字,保留八位得0.000000
	
	float* pFloat = (float*)&n;
	printf("n的值为:%d\n",n);//9
	printf("*pFloat的值为:%f\n", *pFloat);//0.000000

	*pFloat = 9.0;
	//9.0
	//1001.0
	//1.001*2^3
	//(-1)^0*1.001*2^3
	//S=0
	//M=1.001
	//E=3
	//存储到内存中:
	//E=3+127=130
	//0 10000010 00100000000000000000000
	//S     E              M
	//以整数形式打印:1091567616
	
	printf("n的值为:%d\n", n);//1091567616
	printf("*pFloat的值为:%f\n", *pFloat);//9.000000

	return 0;
}

运行结果:

                                                                                                                                                                                                                                                                   

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

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

相关文章

六、机械手的种类

机械手是机器人能够完成指令的一个重要输出装置&#xff0c;机器臂是否合理、有效&#xff0c;决定了机 器人能否发挥出应有的作用。 机械手是一种能模仿人手和臂的某些动作功能&#xff0c;用以按固定程序抓取、搬运物件或操作工具的自动操作装置。特点是可以通过编程来完成各…

wy的leetcode刷题记录_Day68

wy的leetcode刷题记录_Day68 声明 本文章的所有题目信息都来源于leetcode 如有侵权请联系我删掉! 时间&#xff1a;2023-6-6 前言 目录 wy的leetcode刷题记录_Day68声明前言1019. 链表中的下一个更大节点题目介绍思路代码收获 1019. 链表中的下一个更大节点 2352. 相等行列…

CPU、内存、缓存的关系

术语解释 &#xff08;1&#xff09;CPU&#xff08;Central Processing Unit&#xff09; 中央处理器 &#xff08;2&#xff09;内存 内存用于暂时存放CPU中的运算数据&#xff0c;以及与硬盘等外部存储器交换的数据。它是外存与CPU进行沟通的桥梁&#xff0c;内存的运行决定…

Docker容器管理

docker容器相当于一个进程&#xff0c;性能接近于原生&#xff0c;几乎没有损耗&#xff1b; docker容器在单台主机上支持的数量成百上千&#xff1b; 容器与容器之间相互隔离&#xff1b; 镜像是创建容器的基础&#xff0c;可以理解镜像为一个压缩包 docker容器的管理 容…

深耕电力行业,百度智能云助力电厂节煤降耗

山西省吕梁市汾阳市三泉镇&#xff0c;晋能集团旗下山西国峰煤电有限责任公司的两台300MW循环流化床直接空冷机组正在运行&#xff0c;燃煤通过传送带进入锅炉燃烧&#xff0c;将水加热成高温高压蒸汽&#xff0c;用以推动汽轮机拖动发电机旋转发电&#xff0c;支撑工业生产、点…

CW32-Template CW32F030开发板工程模板

国产MCU Embedded-CW32-Board-Template Embedded-CW32-Board-Template CW32-Template第三方资源集合 CW-Template CW32开发者开发板资料 CW32-Board 开发板资料 合集 官方提供的案例Examples CW32F030_StandardPeripheralLib\Examples CW32-48F大学计划板例程 EX1流…

优思学院|精益和六西格玛都强调的一件东西...

精益和六西格玛有着诸多不同&#xff0c;它们的方法和理念也不尽相同&#xff0c;但却有一件东西&#xff0c;是他们的共同理念和工具&#xff0c;那就是----标准。 标准&#xff0c;是企业管理中至关重要的一环。标准&#xff0c;不仅指导着我们对人、物和流程的处理方式&…

2023年鄂州中级职称水测考试什么时候考试?

今天鄂州中级职称水测考试开始打印准考证了&#xff0c;但是只能打印部分专业的水测准考证&#xff0c;按照专业&#xff0c;按照批次打印的。 具体通知如下: 各位考生&#xff1a; 为积极稳妥做好我市晋升中、初级专业技术职称综合系列水平能力测试工作&#xff0c;现按专业分…

【2023 年第十三届 MathorCup 高校数学建模挑战赛】A 题 量子计算机在信用评分卡组合优化中的应用 37页论文及代码

相关信息 &#xff08;1&#xff09;建模思路 【2023 年第十三届 MathorCup 高校数学建模挑战赛】A 题 量子计算机在信用评分卡组合优化中的应用 详细建模过程解析及代码实现 【2023 年第十三届 MathorCup 高校数学建模挑战赛】 B 题 城市轨道交通列车时刻表优化问题 详细建…

2023BR软件、Adobe Bridge下载、安装教程

最后附下载地址 Adobe Bridge CS5 软件是一款功能强大的媒体管理器&#xff0c;它允许您集中访问所有创作资源。 功能介绍 1、可以方便地访问本地PSD、AI、INDD 和 Adobe PDF 文件以及其它 Adobe 和非 Adobe 应用程序文件。 2、可以将资源按照需要拖移到版面中进行预览&…

安卓平台下的即时通讯技术深入解析【实时聊天应用开发实战】

摘要: 本文将详细介绍如何使用安卓开发技术实现一个实时聊天应用。我们将通过构建一个基于安卓平台的聊天应用,演示如何处理用户注册、登录、消息发送和接收等关键功能。文章将涵盖安卓开发的各个方面,包括用户界面设计、后端服务器搭建、网络通信、数据存储和安全性等。读…

spring6

Spring6 1、概述 1.1、Spring是什么&#xff1f; Spring 是一款主流的 Java EE 轻量级开源框架 &#xff0c;Spring 由“Spring 之父”Rod Johnson 提出并创立&#xff0c;其目的是用于简化 Java 企业级应用的开发难度和开发周期。Spring的用途不仅限于服务器端的 开发。从简…

【Mysql数据库从0到1】-入门基础篇--数据库了解

【Mysql数据库从0到1】-入门基础篇--数据库了解 &#x1f53b;一、数据库产生背景&#x1f53b;二、数据库有关概述&#x1f53b;三、数据库访问接口&#x1f53b;四、数据库种类&#x1f53b;五、数据库有关术语&#x1f53b;六、常见DBMS排名&#x1f53b;七、常见数据库介绍…

(三)PUN 2核心内容(待更新)

一、实例化 大多数多人游戏需要创建和同步一些游戏对象。也许它是一个角色、一些单位或怪物&#xff0c;应该出现在房间内的所有客户端上。 PUN 提供了一种方便的方法来做到这一点。 与 Unity 中一样&#xff0c;Instantiate 和 Destroy 用于管理 GameObjects 的生命周期。 PU…

令人惊艳的高效算法盘点(附示例)

令人惊艳的高效算法盘点&#xff08;附示例&#xff09; 在计算机科学领域&#xff0c;算法是解决问题的基石。有些算法&#xff0c;因为其高效性和惊人表现&#xff0c;令人瞩目。本文将为你介绍一些令人惊艳的高效算法&#xff0c;让我们一起来领略这些算法的魅力吧&#xf…

Cesium 热力图

var points []; var width 600; var height 400; var max 100; // 热力图经纬度范围 var latMin 28.364807; var latMax 40.251095; var lonMin 94.389228; var lonMax 108.666357; // 根据热力图图片范围&#xff0c;生成随机热力点和强度值 for (var i 0; i < 30…

淘宝618每日一猜6月6日答案-甄嬛在横店哪里参加的选秀?

淘宝6月6日每日一猜答案是什么&#xff1f;&#xff0c;接下来也会给大家来介绍一下6月6日淘宝大赢家每日一猜的答案。 淘宝每日一猜6月6日答案分享 活动问题&#xff1a;甄嬛在横店哪里参加的选秀 活动答案&#xff1a;【交泰殿】 还有打开手机淘宝&#xff0c;搜索“能省就…

2.MySQL数据库基础

文章目录 &#x1f4e4;1. 数据库的操作&#x1f4e4;&#x1f431;1.1 查看(显示)当前的数据库&#x1f431;&#x1f436;1.2 创建数据库&#x1f436;&#x1f42d;1.3 使用(选中)数据库&#x1f42d;&#x1f439;1.4 删除数据库&#x1f439; ✉️2. 常用数据类型✉️&…

【电能质量扰动】基于ML和DWT的电能质量扰动分类方法研究(Matlab实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

团队管理之性能实施团队日志7

从具体技术问题看流程和管理的问题 今天上午9&#xff1a;38在微信群里看到团队内做脚本调试的同事小w和一个开发的对话。 是说发了一个报文&#xff0c;结果失败了。于是就问这个项目组A的开发小a&#xff0c;小a一看&#xff0c;这不是项目组B的错吗&#xff1f; 这时是10&a…