数据的存储(C语言)

news2024/12/23 11:39:33

数据类型:

要了解数据是如何存储的,我们就得先知道C语言中的数据类型

基本数据类型

基本数据类型,也就是C语言内置类型:

char                 -> 字符型

short                -> 短整型

int                     -> 整型

long                  -> 长整型

long long          -> 更长的整型

float                   -> 单精度浮点型

double               -> 双精度浮点型

数据类型的大小

C语言中可用sizeof操作符来计算数据类型在内存中所占空间的大小,单位是字节

#include <stdio.h>
int main()
{
	printf("%zd\n", sizeof(char)); //char大小
	printf("%zd\n", sizeof(short));//short的大小
	printf("%zd\n", sizeof(int));//int的大小
	printf("%zd\n", sizeof(long));//long的大小
	printf("%zd\n", sizeof(long long));//long long 的大小
	printf("%zd\n", sizeof(float));//float的大小
	printf("%zd\n", sizeof(double));//double的大小

	return 0;
}

数据类型的意义

1、类型决定了要开辟空间的大小

2、类型的大小决定了空间的使用范围

3、类型决定了如何看待内存空间的视角

类型的基本归类

整型家族:

char    

        unsignde char  

        signed char

short

        unsigned short

        signed short

int 

        unsigned int

        signed int

long

        unsigned long

        signed long

long long

        unsigned long long

        signed long long

注:char类型也是整型家族的一员

       char类型在内存中存储的时候,存的是其对应的ASICC码值

        unsigned 表示 无符号数

        signed 表示 有符号数

浮点数家族:

float   单精度浮点型

double  双精度浮点型

构造类型:

构造类型也叫自定义类型

是我们自己所创建的类型

数组类型

结构体类型 struct

联合类型  union
枚举类型  enum

指针类型:

指针类型分类:

整型指针:int*

字符指针:char*

浮点型指针:float*

空类型指针:void*

........

注:空类型的指针可以接收任意类型的指针数据

空类型

void 空类型(无类型)

用途:

        函数的返回类型

        函数的参数

        指针类型

        ......

整型在内存中的存储

整型家族在内存中是以二进制的补码进行存储

那么要了解它的存储,

我们首先要了解清楚,原码、反码、补码的概念

原码、反码、补码

为什么存的是补码?

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

同时,加法和减法也可以统一处理(CPU只有加法器)

此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路

原码 转 补码

原码 —> 反码 —> 补码 (原码转补码)

原码:将整数直接转化成二进制,这个二进制数就是它的原码

反码:将原码的符号位不变,其它位按位取反得到反码

补码:给反码 +1 得到补码

注:转化二进制数的最高位是符号位,其中 0 表示正数,1 表示负数

eg:

#include <stdio.h>

int main()
{
	 int a = 10;

	//定义一个整型变量,在内存中开辟4字节的空间
	//变量名为a,这个空间里面存储10
	//那么数字10是如何存储的呢?
	// 整型家族在存储的时候存的是二进制的补码

	// 先将数字化成二进制 得到原码
	// 00000000 00000000 00000000 00001010   --》原码

	// 再将原码按符号位不变,其它位按位取反得到反码(最高位是符号位,0表示正数,1表示负数)
	// 01111111 11111111 11111111 11110101   --》反码

	//再让反码+1得到补码
	// 01111111 11111111 11111111 11110110   --》补码

	 //而在内存中存的就是二进制的补码

    printf("%d\n",a);

	return 0;
}

补码转原码

方法1: 倒着推回去

补码  —> 反码 —> 原码

先让补码 -1 得到反码,

再让反码符号位不变其它位按位取反得到原码

方法2: 重新按照原码转补码的步骤走一遍

先让补码符号位不变其它位按位取反 得到一个二进制序列

再让这个二进制序列 +1 得到原码

eg:

#include <stdio.h>

int main()
{
	int a = 10;
	// 定义一个整型变量a

	// a的原码:00000000 00000000 00000000 00001010
	// a的反码:01111111 11111111 11111111 11110101
	// a的补码:01111111 11111111 11111111 11110110

	printf("%d\n", a);
	//打印a
	
	//因为在内存中存的是补码,而我们取出来的时候是要用原码,
	//所以 要将补码 转回 成原码
	
	// 方法1:倒着推回去
	// a的补码:01111111 11111111 11111111 11110110
	// 让补码 -1 得到反码
	// a的反码:01111111 11111111 11111111 11110101
	// 让反码符号位不变,其它位按位取反得到原码
	// a的原码:00000000 00000000 00000000 00001010
	// 最后将原码以 %d(十进制数)的形式打印出来


	//方法2:重新按照原码转补码走一遍
	//   a的补码:01111111 11111111 11111111 11110110
	// 让补码符号位不变,其它位按位取反 得到一个二进制序列
	//二进制序列:00000000 00000000 00000000 00001001
	//再让这个二进制序列 +1 得到原码
	//   a的原码:00000000 00000000 00000000 00001010

	return 0;
}

大小端字节序存储

每个机器的存储模式不同,

二进制的补码在存储时 有的机器是从前往后存储,而有的机器是从后王前存储

由于存储顺序不同,就产生了大小端的概念

当前机器是从后往前存储的

大端存储:数据的低权值位保存在内存的高地址处,数据的高权值位保存在内存的低地址处

小端存储:数据的低权值位保存在内存的低地址处,数据的高权值位保存在内存的高地址处

(小端口诀:小小小(低权值位,低地址,小端))

例题:判断当前机器是大端字节序存储,还是小端字节序存储

#include <stdio.h>
int main()
{
	int a = 1;
	//原码:00000000 00000000 00000000 00000001
	//反码:01111111 11111111 11111111 11111110
	//补码:01111111 11111111 11111111 11111111

	//显示的时候是用十六进制显示
	//原码:00000000 00000000 00000000 00000001
//  十六进制显示:00 00 00 01

	// 小端存储:10 00 00 00
	// 大端存储:00 00 00 01

	//将a的第一个字节的地址给b
	// *b取到第一个字节的内容,若为1 是小端,若为0 是大端
	char* b = &a;
	if (*b == 1)
	{
		printf("小端\n");
	}
	else
	{
		printf("大端\n");
	}
	return 0;
}

例题:

//第一题
#include <stdio.h>
int main()
{
	char a = -1;
	//原码:10000001
	//反码:11111110
	//补码:11111111
	signed char b = -1;
//有符号的char取出来的时候还是将补码转为原码
// 取出来的原码:10000001
	unsigned char c = -1;
//无符号char 取出来的时候,认为补码就是原码
//取出来的原码:11111111
	printf("a=%d  b=%d  c=%d\n", a, b, c);// -1  -1  255
	return 0;
}


//第二题
#include <stdio.h>
int main()
{
	char a = -128;
// 原码:10000000
// 反码:01111111
// 补码:10000000
	printf("%u\n", a);//非常大的一个数字
//在取的时候是用无符号整数
//认为补码就是原码
// 10000000   00000000 00000000 00000000 
	return 0;
}


//第三题
#include <stdio.h>
int main()
{
	char a = 128;
	// 正数的原反补相同
	// 补码: 10000000
	printf("%u\n", a);//2^32
	//在取得时候 进行整型提升
	//10000000 00000000 00000000 00000000
	return 0;
}


//第四题
#include <stdio.h>
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
	//补码:11111111 11111111 11111111 11101100
	//      11111111 11111111 11111111 11110110

	//让他们的补码相加,在取的时候是%d 有符号的,所以结果是-10

	//      10000000 00000000 00000000 00001001
	//      10000000 00000000 00000000 00001010   -10
	printf("%d\n", i + j);
	return 0;
}


//第五题
#include <stdio.h>
int main()
{
	unsigned int i = 0;
	for (i = 9; i >= 0; i--)
	{
		printf("%u ", i);
	}
	// 9 8 7 6 5 4 3 2 1 0 
	//当i--变成-1的时候-1的二进制序列为:10000001 
	
	//但因为i是无符号整数,所以在取的时候,会将符号位当做是数位计算进去
	//在打印的时候会发生整型提升,所以会发生死循环!
	// 10000001 0000000 00000000 00000000

	return 0;
}


//第六题
#include <stdio.h>
int main()
{
	char a[1000];
	int i = 0;
	for (i = 0; i < 1000; i++)
	{
		a[i] = -1 - i;
	}
	// -1   ..... -127  128 ...... 0
	// 127+128=255
	//strlen函数遇到’'\0'才停止 而‘\0'的ASICC值是 0 所以遇到0就结束
	printf("%d\n", strlen(a));

	return 0;
}


//第七题
#include <stdio.h>
unsigned char i = 0;
int main()
{
	// 00000000
	// 00000001
	// .......
	// 11111111
    // ......
    // 10000000
	//死循环
	for (i = 0; i <= 255; i++)
	{
		printf("hello world\n");
	}
	return 0;
}

浮点型在内存中存储

  浮点家族:float、double、long double 类型

点数存储的一个例子:

#include <stdio.h>

int main()
{
	int n = 9;
	float* p = (float*)&n;
	
	printf("%d\n", n);//9
	printf("%f\n", *p);//0.000000

	*p = 9.0;

	printf("n的值为:%d\n", n);//1091567616
	printf("*p的值为:%f\n", *p);//9.000000

	return 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.01*2^2

按照IEEE规定则可以看成:(-1)^0  *  1.01  * 2^2

按照公式:S=0,M=1.01,E=2

存储时如何放入内存的呢?

IEEE 754 规定:

1、32位的浮点数的存储:

        最高的1位是符号位S,接着的8位是指数E,剩下的32位是有效数字M

2、 64位浮点数的存储

        最高的1位是符号位S,接着的11位是指数E,剩下的52位是有效数字M

 

IEEE 754,对有效数字M,和指数E 有些特别的规定:

1、对于有效数字M:

因为 1≤M<2  所以M总是 1.xxxx...  的数,

因此1 可以被舍弃,我们在存储的时候只存储M的xxxx...部分,

等到读取的时候在xxx...前面把1加上去

好处:舍弃1这样可以存储24位有效数字

2、对于指数E:

首先E为一个无符号整数:

因为E是科学技术法的指数,所以会出现负数的情况,但是E是一个无符号整数,

所以IEEE 754 规定:E在存入内存时,其真实值必须加上一个中间数

对于8位的E 中间数是 127,对于11位的E 中间数是 1023

eg: 2^10  中 指数E为10,将它保存成32位浮点数,E=10+127=137

      存入内存中:10001001

然而指数E从内存中取出又分为三种情况:

取E的三种情况:

1、E不全为0或不全为1:

        此时取的时候 将E减去中间值(127或1023)得到真实值,

        再将有效数字M前面加上第一位的1

        eg:浮点数0.5 (1/2) 二进制为:0.1 

        转化为:(-1)*0 * 1.0 * 2^-1

         在存储时 给8位E加上中间数127,舍弃M第一位的1,不够位的补0

        S=0,E=-1+127=126,M=1.0

        则存储起来的二进制为:0 01111110 00000000000000000000000

2、E为全0:

        此时取的时候,指数E=1-127或者E=1-1023,即为真实值

        有效数字M不再加上第一位的1,而是还原为 0.xxxx... 的小数

3、E为全1:

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

注:由于浮点数跟整数的存取方式不同,用不同的方式取的时候结果也大不相同

此时我们再回过头来解释刚刚的例题

#include <stdio.h>

int main()
{
	//由于浮点数跟整数的存取方式不同,用不同的方式取的时候结果也大不相同

	int n = 9;//n是整数,存在原反补问题
	//原码:00000000 00000000 00000000 00001001
	//正数的原码、反码、补码相同
	//补码:00000000 00000000 00000000 00001001
	// 内存中存的就是整数的补码

	float* p = (float*)&n;
	//将整型的内存空间强制转化成浮点型

	printf("n = %d\n", n);//9
	// 用%d读取的时候,打印的是有符号十进制整数
	// 由于n是正数,原码、反码、补码都相同
	// 所以用%d打印 结果就是9

	printf("%*p = %f\n", *p);//0.000000
	//用%f读取的时候,打印的是浮点数,
	// 而这片空间里面存储的是:00000000 00000000 00000000 00001001 
	// 用浮点数的存储规则取出来
	// 0 00000000 00000000000000000001001
	// S    E         M
	// 在取的时候,S为0表示正数,E全为0的情况 有效数字M不加第一位的1
	//而在打印的时候%f只能精确到有效数字后六位
	// 所以结果为:0.000000

	*p = 9.0;
	//在p指针指向的空间n里面放入浮点数字9.0
	//9.0的二进制数:1001.0   --  1.001*2^3
	//由浮点数的存储规则可知:
	//(-1)^0 * 1.001 * 2^3
	//S=0,E=3,M=1.001
	//在存储的时候让E加上中间值,让M舍弃第一位的1
	//S=0,E=3+127=130,M=001
	//存入到内存的二进制数,不够位数的补0
	//0 10000010 00100000000000000000000

	printf("n = %d\n", n);//1091567616
	//用%d读取的时候是以整数的形式读取内存中存入的二进制数
	//0 10000010 00100000000000000000000
	//将内存中的二进制数读取为整数结果是:1091567616
	
	printf("n = %f\n", *p);//9.000000
	/*用%f读取的时候读取的是浮点数
	而内存中本身就存入的是浮点数
	打印的结果就是它本身:9.000000*/

	return 0;
}

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

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

相关文章

html textarea 插入字符在光标处

textarea 插入字符在光标处前言深度解析1 效果图上代码前言 深度解析 1 效果图 上代码 <!DOCUMENT><html><head> <link rel"stylesheet" href"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css&qu…

Semantic Segmentation | 评价指标代码解析

如有错误&#xff0c;恳请指出。 文章目录1. 定义解析2. 代码解析之前有记录过关于图像语义分割的相关评价指标与经典网络&#xff0c;在看PointNet的语义分割训练脚本的时候&#xff0c;图像的语义分割和点云的语义分割其实本质上是一致的。所以这里想记录一下语义分割的评价指…

MySQL下载及使用navicat连接mysql数据库(含下载地址、超具体细节、推荐数据库教程)

目录下载地址安装流程第一步&#xff1a;开始安装第二步&#xff1a;类型选择第三步&#xff1a;developer default第四步&#xff1a;execute第五步&#xff1a;服务器配置窗口第六步&#xff1a;网络类型配置窗口第七步&#xff1a;第八步&#xff1a;服务器密码设置窗口第九…

Vue全家桶 Pinia状态管理

Pinia状态管理1. Pinia和Vuex的对比2. 创建Pinia的Store3. Store 简介与使用4. Pinia核心概念State5. Pinia核心概念Getters6. Pinia核心概念ActionsPinia开始于大概2019年&#xff0c;最初是作为一个实验为Vue重新设计状态管理&#xff0c;让它用起来像组合式API&#xff0c;从…

[C语言]初步的来了解一下指针(多图详解)

目录 1.指针是什么 2.指针类型 2.1指针类型的意义(-整数) 2.2指针的解引用 3.野指针 3.1野指针出现的情况 3.2 如何规避野指针 4.指针运算 4.1指针-整数 4.2指针-(减)指针 5.二级指针 1.指针是什么 指针是内存中最小的单元编号&#xff0c;也就是地址。指针还可以是一种…

录屏软件电脑版免费哪个好?4款免费屏幕录制软件下载

在电脑上经常能使用的录屏功能&#xff1a;录制软件的操作过程、精彩的游戏瞬间、经典的影视故事、网络教学等。许多人都在问&#xff0c;录屏软件电脑版哪个好&#xff1f;Windows平台上有很多录屏软件&#xff0c;如果对录屏需求不高的朋友&#xff0c;可以通过内置的视频软件…

08线性回归+基础优化算法

P2基础优化算法 1.最常见的优化算法——梯度下降&#xff0c;用在模型没有显示解的情况下&#xff08;线性回归有显示解&#xff0c;但是现实中很少有这样理想的情况&#xff09; 2.梯度下降的实现方法&#xff1a;沿着反梯度更新方向参数求解 解释&#xff1a; 超参数&#x…

HTTP_day03

当键入网址后&#xff0c;到网页显示&#xff0c;其间发生了什么&#xff08;下&#xff09; 掘金地址 键入 localhost ,通过 Wireshark 抓包分析&#xff0c;抓包结果如下图所示 抓包结果 我们知道 HTTP 协议是运行在 TCP/IP 基础 之上的。 浏览器 通过 HTTP 接收和发送数据…

怎么才能写出好的代码

前言这是一篇如何写好代码的水文&#xff0c;因为最近输出了不少干货&#xff0c;但是大家点赞太少&#xff0c;我越来越没有激情了&#xff0c;那就放放水啦。所以如果大家觉得我的分享对你有用&#xff0c;动动发财小手&#xff0c;赞起来吧&#xff01;虽然是一篇水文&#…

谷粒学苑项目-第一章数据库设计与项目结构-1.1

一、数据库设计 1、数据库 guli2、数据表 CREATE TABLE edu_teacher (id char(19) NOT NULL COMMENT 讲师ID,name varchar(20) NOT NULL COMMENT 讲师姓名,intro varchar(500) NOT NULL DEFAULT COMMENT 讲师简介,career varchar(500) DEFAULT NULL COMMENT 讲师资历,一句话说…

Java--经典九道练习题

文章内容 一、用户登录 二、遍历字符串 三、统计字符个数 四、拼接字符串 五、字符串反转 六、金额转换&#xff08;较难&#xff09; 七、手机号屏蔽 八、身份证号码信息查看 九、游戏骂人敏感词替换 一、用户登录 一直正确的用户名和密码&#xff0c;请用程序实现模…

获取当前进程的启动程序

本文迁移自本人网易博客&#xff0c;写于2012年3月23日&#xff0c;获取当前进程的启动程序 - lysygyy的日志 - 网易博客 (163.com)1.获取当前进程的启动程序CString sFile;GetModuleFileName(NULL, sFile.GetBuffer(), 255);2.获取文件类型3.Autocad文档交互时&#xff0c;使用…

Camera | 1.Camera基础知识

一口君最近在玩瑞芯微的板子&#xff0c;之前写了几篇基于瑞芯微的文章&#xff0c;大家可以学习一下。 《瑞芯微rk356x板子快速上手》 《Linux驱动|rtc-hym8563移植笔记》 《Linux驱动 | Linux内核 RTC时间架构》 《瑞芯微 | 摄像头ov13850移植笔记》 《rk3568 | 瑞芯微平…

图的生成树与生成森林

文章目录连通图与连通分量强连通图与强连通分量图的连通性判断生成树深度优先生成树邻接表邻接矩阵广度优先生成树邻接表邻接矩阵生成森林获取边弧的权值源代码连通图与连通分量 在无向图中, 若从顶点v到顶点w有路径存在, 则称v和w是连通的. 若图G中任意两个顶点都是连通的, 则…

动态规划--矩阵链相乘问题

明确原始问题A[1:n]&#xff1a;计算矩阵链 所需的最小乘法次数。 &#xff08;1&#xff09;是否满足最优子结构&#xff0c;问题的解是否包含子问题的优化解&#xff1f; 若计算A[1:n]的优化顺序在k处断开矩阵链&#xff0c;即A[1:n]A[1:k]A[k1:n],则在A[1:n]的优化顺序中&a…

ReFactor GNN:从消息传递角度重新审视FMs

分享嘉宾 | 陈艺虹 文稿整理 | William Knowledge Graph Completion(KGC) 知识图谱一般会有多个节点&#xff0c;包括性别、国家等各种各样的节点(也可理解为实体)&#xff0c;节点之间会有不同的关系&#xff0c;可以通过其他的一些节点预测出当前节点的其他信息。恢复这些信…

2023/1/6 Vue学习笔记-1

尝试 Vue.js 最简单的方法是使用 Hello World 例子。你可以在浏览器新标签页中打开它&#xff0c;跟着例子学习一些基础用法。或者你也可以创建一个 .html 文件&#xff0c;然后通过如下方式引入 Vue&#xff1a; <!-- 开发环境版本&#xff0c;包含了有帮助的命令行警告 -…

设计模式——建造者模式

文章目录模式理解基本概念使用示例建造者模式延展模式理解 建造者模式&#xff08;Builder Pattern&#xff09;&#xff1a;建造者模式是一种对象创建型模式。将一个复杂对象的构建与它的表示分离&#xff0c;使得同样的构建过程可以创建不同的表示。这句话理解起来太抽象了&…

B. Dima and a Bad XOR(构造 + 异或性质)

Problem - 1151B - Codeforces 来自Kremland的学生Dima有一个大小为nm的非负整数矩阵a。 他希望从矩阵的每一行中选择一个整数&#xff0c;以便所选整数的按位互斥或严格大于0。帮助他! 形式上&#xff0c;他想选择一个整数序列c1,c2&#xff0c;…&#xff0c;cn(1≤cj≤m)&am…

Integer包装类详解(java)

文章目录&#x1f4d6;前言&#xff1a;&#x1f380;包装类概念&#xff1a;&#x1f380;包装类分类&#xff1a;&#x1f380;包装类integer介绍&#xff1a;&#x1f387;自动装箱和自动拆箱问题【⚠注意面试常考点】&#x1f387;Integer常用方法&#xff1a;&#x1f4d6…