【C语言】数据的存储

本章重点
1. 数据类型详细介绍
2. 整形在内存中的存储:原码、反码、补码
3. 大小端字节序介绍及判断
4. 浮点型在内存中的存储解析

Ⅰ、数据类型介绍

前面我们已经学习了基本的内置类型:
空间大小(字节)
char         //字符数据类型                1
short       //短整型                           2
int         //整形                                 4
long         //长整型                           4
long long   //更长的整形                 8
float       //单精度浮点数                 4
double       //双精度浮点数              8
类型的意义:
1. 使用这个类型开辟内存空间的大小(大小决定了使用范围)。
2. 如何看待内存空间的视角。

        1.类型的基本归类:

整形家族:
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 表示空类型(无类型)
通常应用于函数的返回类型、函数的参数、指针类型。

Ⅱ、整形在内存中的存储

一个变量的创建是要在内存中开辟空间的。空间的大小是根据不同的类型而决定的。
接下来我们谈谈数据在所开辟内存中到底是如何存储的。
比如:
int a = 20 ;
int b = - 10 ;
我们知道为 a 分配四个字节的空间。
那如何存储?
下来了解下面的概念:

         1 .原码、反码、补码

计算机中的整数有三种 2 进制表示方法,即原码、反码和补码。
三种表示方法均有 符号位 数值位 两部分,符号位都是用 0 表示 ,用 1 表示 ,而数值位
正数的原、反、补码都相同。
负整数的三种表示方法各不相同。
原码
直接将数值按照正负数的形式翻译成二进制就可以得到原码。
反码
将原码的符号位不变,其他位依次按位取反就可以得到反码。
补码
反码 +1 就得到补码。
对于整形来说:数据存放内存中其实存放的是补码。
在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统一处理;
同时,加法和减法也可以统一处理( CPU 只有加法器 )此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。

我们看看内存中的储存: 

我们可以看到对于 a b 分别存储的是补码。但是我们发现顺序有点 不对劲
这是又为什么?

        2. 大小端介绍

什么大端小端:

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

小端(存储)模式,是指数据的 低位 保存在内存的 低地址 中,而数据的 高位 , ,保存在内存的 高地址 中。
为什么有大端和小端:
为什么会有大小端模式之分呢?这是因为在计算机系统中,我们是以字节为单位的,每个地址单元都对应着一个字节,一个字节为8 bit 。但是在 C 语言中除了 8 bit char 之外,还有 16 bit short型,32 bit long 型(要看具体的编译器),另外,对于位数大于 8 位的处理器,例如 16 位或者 32位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如何将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式。
总的来说: 一个地址单元对应一个字节,那一个数据(int,double...)  就可能涉及到多个地址单元,这时候从哪边开始存数据在不同的专家的观点上就出现了不同的意见,从而出现了大小端。
笔试题:
请简述大端字节序和小端字节序的概念,设计一个小程序来判断当前机器的字节序。( 10 分)
这个题我们用两个代码来看,一个是普通的强制类型转换,一个是联合体,联合体我们后面再聊,今天聊强转类型转换:
我们都知道,一个int占4个字节,当我们用int去存储数字1的时候,他只会用其中一个字节去存储他并且数字 1位于32位中的最低位,将数字 1 存入int后,我们将变量的地址取出来强制类型转换为 char* 。由于(char*)类型指针每次解引用只会访问一个字节,他就会访问 int 变量地址的第一个字节,如果我们得到 1 则说明 最低位存到了地位——小端存储,如果得到的是0,则说明高位上的 0 存到了低位——大端存储;
//代码1
#include <stdio.h>
int check_sys()
{int i = 1;return (*(char *)&i);
}
int main()
{int ret = check_sys();if(ret == 1){printf("小端\n");}else{printf("大端\n");}return 0;
}
//代码2
int check_sys()
{union{int i;char c;}un;un.i = 1;return un.c;
}

3 练习:

1.
//输出什么?
#include <stdio.h>
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;
}
2.
#include <stdio.h>
int main()
{char a = -128;printf("%u\n",a);return 0;
}
3.
#include <stdio.h>
int main()
{char a = 128;printf("%u\n",a);return 0;
}
4.
int i= -20;
unsigned  int  j = 10;
printf("%d\n", i+j); 
//按照补码的形式进行运算,最后格式化成为有符号整数
5.
unsigned int i;
for(i = 9; i >= 0; i--)
{printf("%u\n",i);
}
6.
int main()
{char a[1000];int i;for(i=0; i<1000; i++){a[i] = -1-i;}printf("%d",strlen(a));return 0;
}
7.
#include <stdio.h>
unsigned char i = 0;
int main()
{for(i = 0;i<=255;i++){printf("hello world\n");}return 0;
}

详解:

1.

//练习//输出什么?
#include <stdio.h>
int main()
{char a = -1;//10000001  a在内存中的原码//11111110  a的补码//11111111  a的补码--机器中存储的//11111111 11111111 11111111 11111111  整型提升之后的十进制补码//10000000 00000000 00000000 00000001  取反加一后得到原码--打印出来即a= -1signed char b = -1;//在当前编译器环境下,char默认是 有符号char  所以 b和a打印出来是一样的unsigned char c = -1;	//虽然c是无符号的char,但是他机器中存的依旧是-1的补码,//因为赋值是先将-1存进内存再根据接收类型的需要的需要进行读取//10000001  a在内存中的原码//11111110  a的补码//11111111  a的补码--机器中存储的// //打印c的时候是要转为整形,进行整形提升,整形提升是按照符号位提升的,这里C是无符号数// 记住::::::这里不是看-1  看的是C  //00000000 00000000 00000000 11111111 (无符号) C 进行整形提升后//%d  是按照十进制有符号数打印,他会将最高位看作为符号位 所以打印出来为——255printf("a=%d,b=%d,c=%d", a, b, c);return 0;
}

输出结果

2.

#include <stdio.h>
int main()
{char a = -128;//10000000  补码//11111111 11111111 11111111 10000000  整形提升后的结果//%u是按照10进制无符号数打印  这里将a看作无符号数打印不需要取反加一//按照补码的形式进行运算,最后格式化成为有符号整数printf("%u\n", a);return 0;
}

3.

#include <stdio.h>
int main()
{char a = 128;//此编译器环境下,char默认有符号,128显然超过了有符号char的表示范围,经过编译器处理 a在机器中存的是//10000000  a的机器码//有符号的a整形提升//11111111 11111111 11111111 10000000  整形提升后的结果//按照无符号数打印  按照补码的形式进行运算,最后格式化成为有符号整数printf("%u\n", a);return 0;
}

输出结果跟第二个题一样

4.
int main()
{int i = -20;//10000000 00000000 00000000 00010100 原码//11111111 11111111 11111111 11101100 补码unsigned int  j = 10;//00000000 00000000 00000000 00001010 原码也是补码printf("%d\n", i + j);//11111111 11111111 11111111 11101100 //00000000 00000000 00000000 00001010//11111111 11111111 11111111 11110110 i与j的补码相加// 按照有符号十进制数打印,最高位为1 既要取反加一//10000000 00000000 00000000 00001010 结果 ==== -10//按照补码的形式进行运算,最后格式化成为有符号整数return 0;
}

执行结果:

5.

这个题需要了解一些存储规则,一个数的类型是被他的字节限制住的,超过了他能表示的最大的数他就会截断一部分,相当于转圈圈,这里我们以有符号char与无符号char为例:

有符号数char

无符号数char

然后看我们这个题:

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

无符号数int,他最小的数就是0,转圈圈永远不可能会走到负数,所以这个代码会死循环

通过结果我们还可以看见他也是在慢慢减小,当然这里用的是int不容易看出转圈圈效果,换为char就会看的比较清楚。

输出结果:

6.

这一个题也需要上一个题的铺垫

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

他从-1开始减,有符号char最多可以减到-128,然后再减到0,然后再见到-128,以次循环。不过这里,strlen统计的是 ‘\0' 之前的字符个数,我们第一次走到0的时候就会停止统计(’\0'的ascll码值是0);我们开始计算,-1 ~ -128是128个字符,127 ~ 1 是127个字符(0不统计!统计的是0之前的字符)这样一相加 127+128=255

输出结果:

7.

这个题也是像上面的一样,转圈圈,死循环

#include <stdio.h>
unsigned char i = 0;
int main()
{for (i = 0; i <= 255; i++){printf("hello world\n");}return 0;
}

执行结果:这里为了方便看,我多打印了一个 i :

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

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

         1 .浮点数存储规则

根据国际标准 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
那么,按照上面 V 的格式,可以得出 S=0 M=1.01 E=2
十进制的 -5.0 ,写成二进制是 - 101.0 ,相当于 - 1.01×2^2 。那么, S=1 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
比如:
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
E 全为 0
这时,浮点数的指数 E 等于 1-127 (或者 1-1023 )即为真实值,
有效数字 M 不再加上第一位的 1 ,而是还原为 0.xxxxxx 的小数。这样做是为了表示 ±0 ,以及接近于
0 的很小的数字。
E 全为 1
这时,如果有效数字 M 全为 0 ,表示 ± 无穷大(正负取决于符号位 s );
好了,关于浮点数的表示规则,就说到这里。

 这里给一个浮点数存储例子:

int main()
{float a = 5.5;//101.1//(-1)^0 * 1.011 * 2^2// S=0 E=2+127=129=10000001  M =01100000000000000000000 // 01000000 10110000 0000000000000000 //     40		b0		00  00//  return 0;
}

下面有一个题:

int main()
{int n = 9;//00000000 00000000 00000000 00001001  整数视角存整数 9float* pFloat = (float*)&n;//0 00000000 00000000000000000001001  用浮点数时间看待9//S=0//E=0-127=-127//M= 00000000000000000001001//*pFloat= (-1)^0 * 00000000000000000001001 * 2^(-127)//这个数是远小于0的 而flost只能打印小数点后六位,所以打印结果为  0.000000printf("n的值为:%d\n", n);//9printf("*pFloat的值为:%f\n", *pFloat);//*pFloat = 9.0;//9.0 //(-1)^0   *   1.001  *  2^3  S=0 E=3+127=130= 10000010  M= 00100000000000000000000//01000001 00010000 00000000 00000000  浮点数视角存储 9.0 ——机器码  //  41		  10	   00		00printf("num的值为:%d\n", n);//01000001 00010000 00000000 00000000按照10进制打印为1091567616printf("*pFloat的值为:%f\n", *pFloat);//9.0return 0;
}

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

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

相关文章

别只看影响因子了!又1本毕业神刊偷偷被On Hold了!请谨慎投递

【SciencePub学术】昨日&#xff0c;2023JCR正式发布&#xff0c;现在影响因子的话题依旧是“热搜第一”。大家可以根据自己的研究方向&#xff0c;参考最新发布的JCR报告进行投稿选刊。若大家对于投稿选刊方面有任何问题&#xff0c;都可联系张老师为您解答&#xff01; 相关…

C++之STL(四)

1、迭代器 通过重载*、->、、--等运算符来支持指针操作。 迭代器是容器与算法的桥梁。 2、迭代器的类型 个人理解&#xff0c;迭代器是一个模板类&#xff0c;会根据你传入的类型&#xff0c;生成对应的类型。这个模板类里面存放这该类型的指针&#xff0c;里面重载了*、!…

Boom 3D软件安装包下载-Boom 3D官网最新版下载

众多使用者向我们证明了Boom3D软件31段均衡器和预设&#xff1a;均衡器允许您仅通过手指滑动来调制音频&#xff0c;并将其另存为自定义预设&#xff0c;预设已经精心设计为不同类型的歌曲&#xff0c;因此您能够简单地拨入音频以适应您的心情。相信大家都认同Boom 3D 是一款出…

189.二叉树:将有序数组转换为二叉搜索树(力扣)

代码解决 /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}* Tre…

AI选美大赛揭晓10位入围AI佳丽

全球首届AI小姐选美大赛&#xff08;WAICAs&#xff09;于上月拉开帷幕&#xff0c;总奖池积累到1.6万英镑&#xff0c;1万余名AI创作者参与&#xff0c;报送了超过1500个AI角色参加这场比赛。WAICAs 全称是未来派世界人工智能创造者大奖&#xff0c;由 Fanvue 主办。日前&…

qt打包失败 ,应用程序无法正常启动0xc000007b解决办法

用 windeployqt 打包QT程序&#xff0c;运行时提示程序无法正常启动0xc000007b #原因&#xff1a;因本机装了多个版本的Qt&#xff0c;包括32位&#xff0c;64位的&#xff0c;在cmd下可能是环境变量原因&#xff0c;用 windeployqt 打的包无法运行 解决办法&#xff1a; 1、…

以餐厅为例,来谈谈VOC(客户之声)

VOC&#xff0c;即客户之声&#xff0c;是指通过收集和分析客户的反馈意见&#xff0c;了解他们的需求和期望&#xff0c;进而指导企业改进产品和服务。在餐厅经营中&#xff0c;VOC的应用不仅能够帮助餐厅了解顾客的口味偏好、用餐习惯&#xff0c;还能揭示服务流程中的不足和…

记录一个因 MYSQL 服务端和JDBC驱动版本不一致导致 HMS 启动失败问题

记录一个因 MYSQL 服务端和JDBC驱动版本不一致导致 HMS 启动失败问题 1. 问题现象 某运维同学安装 CDH 后 HIVE 服务报警&#xff0c;查看发现 HS2 的金丝雀检查失败&#xff0c;进一步查看发现&#xff0c;HS2 无法创建默认数据库&#xff0c;且 HMS 启动失败。遂找到笔者进…

vue3中如何使用pinia -- pinia使用教程(一)

vue3中如何使用pinia -- pinia使用教程&#xff08;一&#xff09; 安装使用创建 store使用 store访问修改 store 使用组合式 api 创建 store -- setup storepinia 和 hook 的完美结合如何解决上面的问题 使用 hook 管理全局状态和 pinia 有何优缺点&#xff1f;参考小结 pinia…

上位机图像处理和嵌入式模块部署(mcu之iap升级)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 mcu种类很多&#xff0c;如果是开发的时候需要对固件升级&#xff0c;整体还是比较容易的。不管是dap&#xff0c;还是st-link v2、j-link&#xf…

Modbus协议转Profibus协议网关接温控表与PLC通讯

一、前言 在智能化飞速发展的时代&#xff0c;各个行业都在使用自动化系统。在智能楼宇系统中&#xff0c;温控表和PLC&#xff08;可编程逻辑控制器&#xff09;通讯是至关重要的&#xff0c;在智能楼宇系统中&#xff0c;温控表起着监测和控制室内温度的重要作用。而PLC作为…

Elasticsearch中的Term_Filter过滤器技术

文章目录 一、引言二、Term Filter的工作原理与内部机制三、Term Filter的多样化使用场景3.1 精确匹配3.2 过滤分类与标签3.3 数据范围筛选3.4 复杂查询的构建 四、Term Filter的最佳实践与应用建议4.1 避免使用分析器4.2 优化索引映射4.3 充分利用缓存4.4 持续监控性能 五、结…

【地质灾害监测实现有效预警,44人提前安全转移】

6月13日14时&#xff0c;国信华源地质灾害监测预警系统提前精准预警&#xff0c;安全转移10户44人。 该滑坡隐患点通过科学部署国信华源裂缝计、倾角加速度计、雨量计、预警广播等自动化、智能化监测预警设备&#xff0c;实现了对隐患点裂缝、位移、降雨量等关键要素的实时动态…

PgSQL-添加列、字段的注释

mysql是&#xff1a; 添加列&#xff1a;--alter table 表名 add column 列名 varchar(30);ALTER TABLE p_show ADD COLUMN points VARCHAR(100) COMMENT 所需积分;---------------------------------------------------------------------------------------------添加、修改…

Jmeter性能 之 “查看结果树” 界面功能介绍

前言 查看结果树 显示所有请求响应的树&#xff0c;通过它可以查看任何请求的响应。除了显示响应之外&#xff0c;还可以查看获取响应所花费的时间以及一些响应代码。需要通过"查看结果树"来查看服务器处理请求之后的返回结果&#xff0c;分析是否存在问题 注意&am…

Typora v1.8.6解锁版安装教程 (轻便简洁的Markdown编辑器)

前言 Typora是一款轻便简洁的Markdown编辑器&#xff0c;支持即时渲染技术&#xff0c;这也是与其他Markdown编辑器最显著的区别。即时渲染使得你写Markdown就想是写Word文档一样流畅自如&#xff0c;不像其他编辑器的有编辑栏和显示栏。 一、下载地址 下载链接&#xff1a;…

游戏测试工程师面试,常问的问题有哪些?

一般会在面试中了解以下方面&#xff1a; 1.游戏热情&#xff0c;理解程度 玩过哪些游戏&#xff0c;这些游戏玩过多长时间&#xff0c;玩到什么样的水平&#xff0c;在游戏里花过多少钱 你觉得游戏里&#xff0c;xxx的设计如何&#xff0c;评价一下 2.编程、测试相关 学过哪…

任务3.8.3 利用RDD统计每日新增用户

任务目标 统计给定用户访问历史数据中&#xff0c;每日的新增用户数量。 数据准备 原始数据格式&#xff1a;每行包含两个字段&#xff0c;日期和用户名&#xff0c;以逗号分隔。示例数据&#xff1a;2024-05-01,mike 2024-05-01,alice 2024-05-01,brown ...解决方案 使用倒…

【2024.6.21】今日科技时事:科技前沿大事件

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 目录 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌…

Notepad++插件 Hex-Edit

Nptepad有个Hex文件查看器&#xff0c;苦于每次打开文件需要手动开插件显示Hex&#xff0c;配置一下插件便可实现打开即调用 关联多个二进制文件&#xff0c;一打开就使用插件的方法&#xff0c;原来是使用空格分割&#xff01;&#xff01;&#xff01;