【C进阶】数据在内存中的存储

news2024/9/20 16:51:51

数据在内存中的存储

  • 前言
  • 一、数据类型介绍
    • (一)基本概念
    • (二)类型的基本归类
      • 1.整型家族
      • 2.浮点型家族
      • 3.构造类型
      • 4.指针类型
      • 5.空类型
  • 二、整形在内存中的存储
    • (一)原码、反码、补码
      • 1.概念
      • 2.为什么内存中存的是补码
        • (1)原因
        • (2)例子
        • (3)引例解释
    • (二)大小端介绍
      • 1.什么是大段小端
      • 2.为什么有大端和小端
      • 3.笔试练习题
        • (1)题目描述
        • (2)解题思路
        • (3)代码
    • (三)练习
      • 1.signed char 和 unsigned char
      • 2.打印%u
      • 3.signed+unsigned
      • 4.无符号死循环打印
      • 5.strlen找字符串长度
      • 6.unsigned范围
  • 三、浮点型在内存中的存储
    • (一)引例
    • (二)浮点数存储规则
      • 1.概念
      • 2.举例
      • 3.IEEE 754
        • (1)通项
        • (2)特别规定
          • (i)对于存储M
          • (ii)对于存储E
          • (iii)对于取出E
        • (3)综合举例
    • (三)引例解释
      • 1.第一部分解释
      • 2.第二部分解释
  • 总结


前言

当大家学完C语言的初阶,那肯定要去学习学习C语言进阶,在学习C语言进阶的时候,免不了要先了解一下数据在内存中是怎么存储的,在之前的博客中我也简单讲解了一下关于数据在内存中的存储,相信大家也已经了解了很多关于数据在内存中的存储,那接下来,我将细致地讲解一下关于数据在内存中的存储。


一、数据类型介绍

(一)基本概念

关于数据类型,我们已经学习了内置的学习类型如下:

在这里插入图片描述

类型的意义:

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

(二)类型的基本归类

1.整型家族

在这里插入图片描述
大家可以看,char也属于整型家族,因为我们之前说到过了,char在计算机内存中存储的是ASCII码的值,而ASCII码值为整数,所以char也属于整型家族。另外,除了char其他所有的整型家族的类型前面都是可以忽略signed的,因为这两者是相等的,而char是取决于编译器的,但大多数编译器都是char num; == signed char num;这是十分重要的。

2.浮点型家族

在这里插入图片描述

3.构造类型

也叫做自定义类型。可以根据程序员改变而改变的。
在这里插入图片描述

4.指针类型

在这里插入图片描述

5.空类型

在这里插入图片描述


二、整形在内存中的存储

在进行了解整型在内存中的存储中,先引一个例子:
在这里插入图片描述
这串代码中的a和b在计算机内存是如何存储的呢?我们继续往下看。

(一)原码、反码、补码

1.概念

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

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

大家也可以先去看看下面放的这篇博客,里面详细地讲解了原码、补码、反码的知识。
【C初阶】详解操作符

2.为什么内存中存的是补码

(1)原因

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

(2)例子

根据原码、反码、补码的知识用以计算1-1的值:
在这里插入图片描述

(3)引例解释

在这里插入图片描述
我们可以看到对于a和b分别存储的是补码,那我们经过一系列的操作发现这怎么按照补码来看十六进制是0x00000014和0xfffffff6,而在内存中存的是倒过来的呢?这就涉及到了大端和小端的概念了。

(二)大小端介绍

1.什么是大段小端

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

解释:当你有100块的时候,恰巧碰到一个机会,能够改变1 0 0这三位数字中的任何一位,那我们肯定想改变的是最左边的那位的数字,因为这位数字在最高位,所以从左往右是高字节到低字节。同样,大家也可以理解成我们通常所说的高八位和低八位的区别,高八位就是左边的八位数字,而低八位就是右边的数字。

在这里插入图片描述
我们看看VS2022编译器里是什么端存储的吧!
在这里插入图片描述
原来VS2022IDE编译器是小端存储呀!!!
接下来大家可以看一下函数的栈帧的创建与销毁,能更好的理解为什么VS2022IDE编译器是小端存储的,因为我们在栈区是需要进行压栈和出栈的。
函数栈帧创建与销毁
在这里插入图片描述
当我们进行压栈的时候,是把低字节先压进去,再压高字节;当进行出栈的时候,是把高字节拿出去,再拿低字节,这是由于栈区底下是封死的,而顶上是开放的,出栈的时候必须先拿出高地址的数据,再把低地址的数据拿出去。
小端存储和大端存储没有优劣之分,两者都是好把手!

2.为什么有大端和小端

这是因为在计算机系统中,我们是以字节为单位的,每个地址单元
都对应着一个字节,一个字节为8bit。但是在C语言中除了8bit的char之外,还有16bit的short型,32bit的long型(要看具体的编译器),另外,对于位数大于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如何将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式。
例如:一个 16bit 的 short 型 x ,在内存中的地址为 0x0010 ,x 的值为 0x1122 ,那么 0x11为高字节, 0x22为低字节。对于大端模式,就将 0x11 放在低地址中,即 0x0010 中, 0x22 放在高地址中,即 0x0011(往后增加了一位地址,因为0x1122中的0x11和0x22正好差8个比特位,正好是一个字节) 中。小端模式,刚好相反。我们常用的 X86 结构是小端模式,而 KEIL C51 则为大端模式。很多的ARM,DSP都为小端模式。有些ARM处理器还可以由硬件来选择是大端模式还是小端模式。

3.笔试练习题

(1)题目描述

请简述大端字节序和小端字节序的概念,设计一个小程序来判断当前机器的字节序。

(2)解题思路

概念很简单,就是前面所说的大小端的概念,大端是高字节存放在低地址处,低字节存放在高地址处;小端则相反,而难的是写一个代码判断当前机器的字节序。
那我们想的是利用内存去判断一下,那我们用1来试一下,因为1在内存中用16进制显示的是0x00 00 00 01,我们可以看出1的高字节和低字节是不一样的,那我们如果用小端的方式第一个字节输出的是0x01,而如果是大端的方式存储的话第一个字节输出的是0x00,如下图,那可是怎么取出第一个字节呢?很简单,取地址a,我们先取出int类型的a的首地址,然后再强制类型转换成加一只能跳过一个字节的char类型,然后再存放在一个char*的指针里面即可。
在这里插入图片描述

(3)代码

#include<stdio.h>

int check_sys(void) {
	int a = 1;
	char* p = (char*)&a;
	if (*p == 1)//*p==0x01
		return 1;
	else //*p==0x00
		return 0;
}

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

(三)练习

大家在进行练习之前,强烈建议大家去看一看我之前的博客关于操作符的详解,我会放到下面链接。

1.signed char 和 unsigned char

【C初阶】详解操作符

int main()
{
	char a = -1;
	//-1 是整数 32bit位
	//10000000000000000000000000000001 - 原码
	//11111111111111111111111111111110 - 反码
	//11111111111111111111111111111111 - 补码
	//因为char只占8个bit位,所以发生截断,只保留低8位
	//11111111 - a - 截断
	//因为打印的是%d,是有符号的整数,所以要发生整型提升,按照符号位提升
	//11111111111111111111111111111111 - 补码 - 提升
	//10000000000000000000000000000001 - 原码
	//-1
	signed char b = -1;
	//signed char 与 char是一样的
	unsigned char c = -1;
	//10000000000000000000000000000001 - 原码
	//11111111111111111111111111111110 - 反码
	//11111111111111111111111111111111 - 补码
	//11111111 - c - 截断
	//因为是unsigned char,最高的一位是看做有效位来进行计算的,不是符号位
	//00000000000000000000000011111111 - 补码 - 整型提升
	//00000000000000000000000011111111 - 原码
	//255
	printf("a=%d b=%d c=%d", a, b, c); //-1 -1 255
	//%d是打印有符号的整数

	return 0;
}

这题的注意事项是在进行完截断以后,我们进行整型提升的时候是要看原本这个数的类型进行整型提升的。

2.打印%u

#include <stdio.h>
int main()
{
	char a = -128;
	//10000000000000000000000010000000 - 原码
	//11111111111111111111111101111111 - 反码
	//11111111111111111111111110000000 - 补码
	//截断,char只能保留8个比特位
	//10000000 - a
	//看a本身的类型是signed char,是有符号的char,那么就按照符号位进行整型提升
	//11111111111111111111111110000000 - 整型提升
	//因为%u打印的是无符号的整形,那么就不会把原本的补码看做是一个有符号的整型,而是看做一个无符号的整型,直接打印
	//4294967168
	printf("%u\n", a);
	//%u打印的是无符号整形

	char b = 128;
	//00000000000000000000000010000000 - 原码、反码、补码
	//因为char b只存8个bit位,所以只存低八位
	//100000000 - 截断
	//char 为有符号整型,所以根据符号位进行整型提升
	//11111111111111111111111110000000 - 整型提升
	//因为%u打印的是无符号的整形,那么就不会把原本的补码看做是一个有符号的整型,而是看做一个无符号的整型,直接打印
	//4294967168
	printf("%u\n", b);
	//%u打印的是无符号整形

	return 0;
}

这里要注意的是当我们看到要打印%u的时候,它是会打印一个无符号的整型,所以当我们进行整型提升的时候是不用看符号位的,直接根据它原本的值进行打印,而大家会有疑惑了,为什么不用看符号位直接打印,那它的补码形式怎么办?其实,在计算机内部它存的确实是二进制的补码,但是当我们要打印的是%u这个无符号整型的时候,是直接把原本的补码看做一个无符号的数了,那也就在%u打印的逻辑下不存在补码这回事,它仅仅是一个无符号的整型,而发声整型提升是要看这个数原本的类型的,如果是有符号,则根据符号进行整型提升,如果是无符号,则前面全部补0。

3.signed+unsigned

#include<stdio.h>

int main() {
	int i = -20;
	//10000000000000000000000000010100 - 原码
	//11111111111111111111111111101011 - 反码
	//11111111111111111111111111101100 - 补码
	unsigned int j = 10;
	//00000000000000000000000000001010 - 原码、补码、反码

	printf("%d\n", i + j);
	//%d是打印有符号整型
	//i + j
	//11111111111111111111111111110110 - 补码
	//11111111111111111111111111110101 - -1
	//10000000000000000000000000001010 - 原码
	//-10

	return 0;
}

4.无符号死循环打印

#include<stdio.h>
#include<windows.h>

int main() {
	unsigned int i;//无符号整数,i永远大于等于0
	for (i = 9; i >= 0; i--) //结束循环的条件为i>=0,而unsigned是永远大于等于0
	{
		printf("%u\n", i);
		Sleep(1000);
	}

	return 0;
}

在这里插入图片描述

5.strlen找字符串长度

#include<stdio.h>

int main()
{
	char a[1000];
	int i;
	for (i = 0; i < 1000; i++)
	{
		a[i] = -1 - i;
	}
	//在字符0之前已经相加等于0,128+127=255
	//-1 -2 -3 -4 -5 …… -128 127 …… 5 4 3 2 1 0 -1 -2 ……
	printf("%d", strlen(a));//strlen找'\0'才停止,而'\0'的ASCII码值就为0
	return 0;
}

6.unsigned范围

#include <stdio.h>

unsigned char i = 0; //全局变量unsigned无符号,取值范围为0~255
int main()
{
	for (i = 0; i <= 255; i++)//跳出循环条件恒成立
	{
		printf("hello world\n");
	}
	return 0;
}

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

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

对于整型的取值范围:limits.h可以看到。
对于浮点型的取值范围:float.h可以看到。
大家可以打开everything并用VS code去查看,也可以看我下面的演示图片:

在这里插入图片描述
在这里插入图片描述

(一)引例

在这里插入图片描述
num 和 *pFloat 在内存中明明是同一个数,为什么浮点数和整数的解读结果会差别这么大?要理解这个结果,一定要搞懂浮点数在计算机内部的表示方法这数字有点不符合我们之前猜的数,这说明浮点型的存储方式与整型的存储方式是不一样的!那我们接下来继续往下看吧!

(二)浮点数存储规则

1.概念

在这里插入图片描述

2.举例

在这里插入图片描述

3.IEEE 754

(1)通项

IEEE 754规定:
对于32位的浮点数,最高的1位是符号位s,接着的8位是指数E,剩下的23位为有效数字M。

在这里插入图片描述

对于64位的浮点数,最高的1位是符号位S,接着的11位是指数E,剩下的52位为有效数字M。
在这里插入图片描述

(2)特别规定

(i)对于存储M

因为 1≤M<2 ,也就是说,M可以写成 1.xxxxxxx的形式,其中xxxxxxx表示小数部分。IEEE 754规定,在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的xxxxxxx部分。比如保存1.01的时候,只需要保存01,等到读取的时候,再把第一位的1加上去。这样做的目的,是节省1位有效数字。以32位浮点数为例,留给M只有23位,将第一位的1舍去以后,等于可以保存24位有效数字。相当于节省了一定的空间。

(ii)对于存储E

首先,E为一个无符号整数(unsigned int)。
这意味着,如果E为8位,它的取值范围为0-255;如果E为11位,它的取值范围为0~2047。但是,我们知道,科学计数法中的E是可以出现负数的,所以IEEE 754规定,存入内存时E的真实值必须再加上一个中间数,对于8位的E,这个中间数是127(2的7次-1);对于11位的E,这个中间数是1023(2的10次-1)。比如,2^10的E是10,所以保存成32位浮点数时,必须保存成10+127=137,即10001001。

(iii)对于取出E

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

(3)综合举例

在这里插入图片描述

(三)引例解释

#include<stdio.h>

int main()
{
	int n = 9;
	//00000000000000000000000000001001 - 原码、补码、反码
	float* pFloat = (float*)&n;
	//在*pFloat视角下是浮点型存储方式
	//0 00000000 00000000000000000001001
	//s    e               m
	//v = (-1)^0 * 0.00000000000000000001001 * 2^(-126)
	//0.000000
	printf("n的值为:%d\n", n);//9
	printf("*pFloat的值为:%f\n", *pFloat);//0.000000

	*pFloat = 9.0;
	//1001.0
	//v = (-1)^0 * 1.001 * 2^3
	//s=0
	//e=3   3+127=130(十进制)=10000010
	//m=1.001
	//0 10000010 00100000000000000000000
	//在n的视角下,上式为01000001000100000000000000000000为补码,正数原码补码反码一样
	//所以n为1091567616
	//在*pFloat的视角下:还原以后就是9.0
	printf("num的值为:%d\n", n);//1091567616
	printf("*pFloat的值为:%f\n", *pFloat);//9.0
	return 0;
}

1.第一部分解释

根据E全为0的那个解释,可以知道这个数是一个很小的数,接近于0的数。

2.第二部分解释

n的视角下它是一个有符号的整型,是利用整型的存储模式进行计算的。


总结

关于数据在内存中的存储似乎不是那么难,我们讲解了关于数据类型的基本归类,也涉及到了大小端的概念,此外还讲解了关于浮点型在内存中的存储,这些都是未来能够进行更好的编译的一个很牢固的基础。

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

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

相关文章

android input 事件分发 --- 注册input

android input 事件分发 --- 注册input应用注册input事件应用注册input事件 应用如果要监听input的事件&#xff0c;那么肯定就存在一个注册监听input事件的过程&#xff0c;跟随着addView方法我们跟着走一下frameworks/base/core/java/android/view/WindowManagerImpl.java Ov…

Centos7 安装 MongoDB

使用docker安装Mongo 1、拉取镜像 注&#xff1a;需要科学上网 docker pull mongo [rootlocalhost ~]# docker pull mongo Using default tag: latest latest: Pulling from library/mongo 846c0b181fff: Pull complete ef773e84b43a: Pull complete 2bfad1efb664: Pull co…

LeetCode:14. 最长公共前缀

14. 最长公共前缀1&#xff09;题目2&#xff09;思路3&#xff09;代码4&#xff09;结果1&#xff09;题目 编写一个函数来查找字符串数组中的最长公共前缀。 如果不存在公共前缀&#xff0c;返回空字符串 ""。 示例 1&#xff1a; 输入&#xff1a;strs [“flo…

python常用快捷键

一、编辑&#xff08;Editing&#xff09;Ctrl Space 基本的代码完成&#xff08;类、方法、属性&#xff09;Ctrl Alt Space 快速导入任意类Ctrl Shift Enter 语句完成Ctrl P 参数信息&#xff08;在方法中调用参数&#xff09;Ctrl Q 快速查看文档F1 外部文档Shift F…

c语言 通讯录 动态内存开辟

通讯录 通讯录中能够存放1000人信息 每个人信息&#xff1a; 姓名年龄性别电话地址 2.增加人的信息 3.删除指定人的信息 4修改指定人的信息 5.查找指定人的信息 6.排序通讯录的信息 contact.h 放头文件的声明 tset.c 测试通讯录模块 contact.c 函数的实现 test.c #include &…

基于不同操作系统升级知行之桥的常见问题

此前的文章知行之桥2022版本升级之页面变化以及监控邮件答疑给大家分享了一些升级到知行之桥最新版本关于Web页面显示和监控邮件的一些问题&#xff0c;本篇将分享一些windows和Linux不同操作系统升级部署知行之桥最新版本的一些Q&A。 EDI服务器是windows服务器 Windows操…

Smart JavaScript UI 14.4.0 Crack【htmlelements】

Smart是一个建立在 JavaScript、HTML 和 CSS 之上的全面且创新的 UI 库。Ω578867473使开发人员能够交付专业的、跨浏览器兼容的 Web 应用程序&#xff0c;同时显着缩短他们的开发时间。Smart HTML Elements 包含 60 多个 UI 组件&#xff0c;是 Web 上发展最快的 JavaScript U…

Centos7配置阿里云yum源及epel源

Background 踩坑记录吧。下次可以直接复制粘贴&#xff0c;不用再去排查是哪个字母字符少了多了&#xff0c;我这都是执行成功的命令粘贴过来的。 1、基础知识简介 yum: 全称“Yellow dog Updater, Modified”&#xff0c;是一个专门为了解决包的依赖关系而存在的软件包管理器…

ubuntu 18.04 Pytorch安装GPU版本

先上成功的图片 过程总结&#xff1a; 之前也配置过&#xff0c;但是时间流逝&#xff0c;之前的方法也过时了&#xff08;旧方法一般会提到先装nvidia驱动&#xff0c;再cuda&#xff0c;再cudnn之类&#xff09;。 我今天尝试安装cuda 11.7时&#xff0c;发现cuda会自动安装…

使用 dict 对象创建多重索引 DataFrame

使用 dict 对象创建多重索引 DataFrame创作背景查看所需 dict 的格式结尾创作背景 本菜鸡最近碰到了需要使用字典创建多重索引 DataFrame 的场景&#xff0c;谨以本文记录解决过程。 如果觉得我这篇文章写的好的话&#xff0c;能不能给我 点个赞 &#xff0c;评论 、收藏 一条…

对近似算法概念的学习

近似算法基本概念可近似分类最小顶点覆盖问题近似算法的分析&#xff1a;多机调度问题近似算法贪心G-MPS近似算法递降贪心法DG-MPS货郎问题最近邻NN算法最小生成树法MST最小权匹配MM算法0-1背包问题贪心G-KK多项式近似方案完全多项式时间的近似方案背包问题的对偶问题总结基本概…

彻底搞懂UML图

用例图 用例图的结构主要分为三个部分&#xff1a;参与者、用例、参与者与用例之间的关系。 参与者&#xff1a;不是特指人&#xff0c;是指系统以外的&#xff0c;在使用系统或与系统交互中所扮演的角色。因此参与者可以是人&#xff0c;可以是事物&#xff0c;也可以是时间…

关于alpine如何制作JDK镜像

Docker制作jdk镜像(v1.0)1.1首先编写Dockerfile文件#1.指定基础镜像&#xff0c;并且必须是第一条指令RROM centos:7#2.指明该镜像的作者和其电子邮件MAINTAINER xnx "zwcqq.com"#3.在构建镜像时&#xff0c;指定镜像的工作目录&#xff0c;之后的命令都是基于此工作…

Python自制简易版计算器小程序

前言 今天来给你们分享一个自制的桌面小程序【简易版计算器】 文章内有效果展示&#xff0c;你们可以自行看看哦 不想看文章&#xff0c;想直接领取源码的话&#xff0c;可以直接点击文章末尾的名片哈 环境准备 Python 3.6 Pycharm 代码展示 界面设置 导入模块 所有 源码 …

STM32读取SHT3x系列温湿度传感器,标准库和HAL库

STM32读取SHT3x系列(SHT30、SHT31、SHT35)温湿度传感器的数据并显示在0.96寸OLED屏上。 我下面提供两份代码&#xff0c;一份是标准库使用硬件I2C的&#xff0c;另一份是HAL库使用软件模拟IIC的。 我用的单片机是STM32F103C8T6&#xff0c;温湿度传感器是SHT30。 STM32软件I…

Spring Cloud与Nacos部署Spring Boot项目

本文记录一个用于Spring Cloud 和nacos的一个项目配置方案 本文主要通过nacos实现了两点功能&#xff1a; 1、服务的注册与发现 2、nacos的动态配置 项目中主要包括了nacos-consumer和nacos-provider&#xff0c;一个消费者&#xff0c;一个生产者的角色&#xff0c;消费者负责…

旋转目标检测复现-yolov5-obb

复现源码&#xff1a; https://github.com/hukaixuan19970627/yolov5_obb 亲测可行 安装流程&#xff1a; 按照https://github.com/hukaixuan19970627/yolov5_obb/blob/master/docs/install.md 确保安装过程不报错&#xff0c;否则影响后续训练 安装成功即可准备数据集 hf_t…

MySQL复制技术方案——组复制

1&#xff0e;原理 增强半同步复制虽然解决了HA切换之后的幻读问题&#xff0c;也从一定程度上使得主从实例 之间的数据一致性保障得到增强&#xff0c;但是仍然还有很多问题需要解决。 例如&#xff1a; HA切换程度需要依赖于MySQL服务器之外的第三方程序实现&#xff0c;维…

2022年12月安全事件盘点

2022年12月安全事件盘点 一、基本信息 2022年12月安全事件共造成约8327万美元损失&#xff0c;金额数量较上月有所下降。本月RugPull数量基本与上月持平。但临近年底&#xff0c;熊市社媒诈骗等较上个月有所增加&#xff0c;Discord攻击诈骗成为重灾区。另外本月依然有钱包&…

产线工控设备安全现状分析

工控设备安全现状 工业控制系统是支撑国民经济的重要设施&#xff0c;是工业领域的神经中枢。现在工业控制系统已经广泛应用于电力、通信、化工、交通、航天等工业领域&#xff0c;支撑起国计民生的关键基础设施。 随着传统的工业转型&#xff0c;数字化、网络化和智能化的工…