深度剖析数据在计算机内存中的存储

news2024/11/18 14:33:07

本章重点

1.计算机中数据类型详细介绍
2.整形在内存中的存储方式
3.大小端字节序介绍以及判断方法

1. 数据类型详细介绍

C语言中我们学习的内置类型数据有以下几种。

类型数据类型名称占用内存空间字节
char
字符数据类型
1
short
短整型
2
int
整形
4
long
长整型
4
long long
更长的整形
8
float
单精度浮点数
4
double
双精度浮点数
8

类型的意义:使用这个类型开辟内存空间的大小(大小决定了使用范围)。

在VS2019下观察内置类型数据的大小

#include<stdio.h>
#include<limits.h>

int main()
{
	INT_MAX;

	return 0;
}

limist这个库函数可以查看内置类型数据最大值与最小值

右键点击然后转到定义

通过转到定义可以清晰的查看内置数据的最大值与最小值

整形家族分类

charsigned charunsigned char
shor
unsigned short
signed short [int]
int
unsigned int
signed int
long
unsigned long [int]
signed long [int]

为什么char是属于整形家族的呢?因为char类型在内存中存储的是ASCII码值,是整形,因此归类为整形家族。

对整形家族具有无符号有符号的区分 那么char是unsigned char 还是signed char呢?

这是不确定的,但在VS2019上 char=signed char  short=signed short   int=signed int

2. 整形在内存中的存储:原码、反码、补码

一个变量在内存中存储是需要开辟空间的,而开辟的空间大小取决于变量的数据类型。

#include<stdio.h>
#include<limits.h>

int main()
{
	int a = -10;
	10000000 00000000 00000000 00001010--源码
	11111111 11111111 11111111 11110101--反码
	11111111 11111111 11111111 11110110——补码

	int b = 20;
	00000000 00000000 00000000 00010100——源码==反码==补码
	return 0;
}

int开辟4个字节空间大小,4个字节=32个比特位(byte)。

而计算机中表达二进制数有三种方法 源码  反码 补码

三种表达方法均有符号位数值位两部分组成  0表示正 1表示负  而正数的源反补都相同

以下是8个比特位的内存空间所放二进制数值的所有可能以及转换。

有符号:signed 转换图

无符号:unsigned 转换图

则负数的源 反 补表达方式不同

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

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

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

补码到源码可以有两种方式获取

先取反+1 or 先-1取反

3. 大小端字节序介绍及判断

对于整形来说: 数据存放内存中其实存放的是补码。 
为什么呢?
在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统
一处理; 同时,加法和减法也可以统一处理(CPU 只有加法器 )此外,补码与原码相互转换,其运算过程 是相同的,不需要额外的硬件电路。
通过调试查看内存存储空间
#include<stdio.h>
#include<limits.h>

int main()
{
	int a = -10;
	10000000 00000000 00000000 00001010--源码
	11111111 11111111 11111111 11110101--反码
	11111111 11111111 11111111 11110110——补码
	ff ff ff f6

	int b = 20;
	00000000 00000000 00000000 00010100——源码==反码==补码
	00 00 00 14
	return 0;
}
a的地址
b的地址
a的补码是ff ff ff f6  b的补码是00 00 00 14  内存中存储的值是十六进制
通过调试发现内存存储的顺序是反的?这又是为什么呢?
这时候就要介绍大小端了。
大端(存储)模式,是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址
中;
小端(存储)模式,是指数据的低位保存在内存的低地址中,而数据的高位,,保存在内存的高地
址中。
FF FF FF F6 
万 千  百  个 位
00 00 00 14
万 千  百  个 位
为什么会有大小端?
为什么会有大小端模式之分呢? 这因为在计算机系统中,我们是以字节为单位的,每个地址单元都对应着一个字节,一个字节为8 bit。但是在C语言中除了8 bit的char之外,还有16 bit的short型,32 bit的long型 (要看具体的编译器),另外,对于位数大于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如何将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式
例如:一个16bit的 short型x,在内存中的地址为 0x0010,x的值为 0x1122,那么0x11为高字节,0x22为低字节。对于大端模式,就将 0x11 放在低地址中,即 0x0010 中,0x22 放在高地址中,即 0x0011中。小端模式,刚好相反。我们常用的X86 结构是小端模式,而 KEIL C51则为大端模式。很多的ARM,DSP都为小端模式。有些ARM处理器还可以由硬件来选择是大端模式还是小端模式。

怎么判断机器字节序是大端还是小端呢?

#include<stdio.h>
int check_key()
{
	int n = 1;
	00000000 00000000 00000000 00000001
	 00  00 00 01 小端字节 01 00 00 00 大端字节 00 00 00 01

	char b = *(char*)&n;

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

	return 0;
}

我们先强制类型转换成char类型再取n的地址,再解引用访问地址,如果是1就是小端 否则大端。

4.代码习题

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

答案是 -1 -1 255    为什么呢?

#include <stdio.h>
int main()
{
	char a = -1;
	10000000 00000000 00000000 00000001——源码
	11111111 11111111 11111111 11111110——反码
	11111111 11111111 11111111 11111111——补码 ——截断

	11111111 -a
	整形提升
	11111111 11111111 11111111 11111111 ——补码
	10000000 00000000 00000000 00000000 ——取反
	10000000 00000000 00000000 00000001 ——源码
	signed char b = -1;
	10000000 00000000 00000000 00000001——源码
	11111111 11111111 11111111 11111110——反码
	11111111 11111111 11111111 11111111——补码 ——截断
	整形提升
	11111111 11111111 11111111 11111111 ——补码
	10000000 00000000 00000000 00000000 ——取反
	10000000 00000000 00000000 00000001 ——源码
	unsigned char c = -1;
	10000000 00000000 00000000 00000001——源码
	11111111 11111111 11111111 11111110——反码
	11111111 11111111 11111111 11111111——补码 ——截断
	整形提升
	11111111
	00000000 00000000 00000000 11111111 ——正数 源反补相同
	printf("a=%d,b=%d,c=%d", a, b, c);%d打印的是十进制数字 输出的是源码的值
	-1 -1 255
	return 0;
}

为什么会整形提升和截断呢?
C 的整型算术运算总是至少以缺省整型类型的精度来进行的。
为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型
这种转换称为整型 提升
整型提升的意义:
表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度一般就是int的字节长度,同时也是CPU的通用寄存器的长度。
因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度。
通用CPU (general-purpose CPU) 是难以直接实现两个8比特字节直接相加运算 (虽然机器指令中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值都必须先转换为int或unsigned int,然后才能送入CPU去执行运算。
负数char的整形提升:一般高位补充符号位
无符号char的整形提升:一般高位补充0
char a = -1;
1000000 0000000 0000000 00000001 ——源码
1111111 1111111 1111111 11111110 ——反码
1111111 1111111 1111111 11111111 ——补码 因为char只有一个字节大小进行截断

11111111 -a 截断
整形提升
1111111 1111111 1111111 11111111


unsigned char a = -1;
1000000 0000000 0000000 00000001 ——源码
1111111 1111111 1111111 11111110 ——反码
1111111 1111111 1111111 11111111 ——补码 因为char只有一个字节大小进行截断

11111111 -a 截断
整形提升
00000000 00000000 00000000 11111111 为正数 源反码相同

2.第二道习题

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

答案输出4,294,967,168 为什么呢?

因为是无符号数 没有符号位 全是数值位

不相信的同学 可以win+R 输出calc 弹出计算器切换到程序员

把整形提升后的二进制序列输入进去 就可以看到结果了。

#include <stdio.h>
int main()
{
	char a = -128;
	1000000 0000000 0000000 10000000 ——源码
	1111111 1111111 1111111 01111111 ——反码
	1111111 1111111 1111111 10000000 ——补码 截断
	
	10000000 -a
	整形提升
	11111111 11111111 11111111 10000000
	printf("%u\n", a);
	4,294,967,168
	return 0;
}

3..第三道习题

这个输出什么?
#include <stdio.h>
int main()
{
    char a = 128;
    printf("%u\n",a);
    return 0;
}

 答案还是4,294,967,168

#include <stdio.h>
int main()
{
	char a = 128;
	00000000 00000000 00000000 10000000——正数 源反码相同
	截断10000000

	整形提升
	11111111 11111111 11111111 10000000
	printf("%u\n", a);
	return 0;
}

原理与上题相同 

4.第四道习题

输出什么?
#include<stdio.h>
int main()
{
int i= -20;
unsigned int j = 10;
printf("%d\n", i+j); 

}

答案是-10

#include <stdio.h>
int main()
{
	int i = -20;
	1000000 00000000 00000000 00010100 ——源码
	11111111 11111111 11111111 11101011 ——反码
	11111111 11111111 11111111 11101100 ——补码
	unsigned int j = 10;
	00000000 00000000 00000000  00001010 ——正数 源反码相同

	相加补码
	11111111 11111111 11111111 11101100 -i
	00000000 00000000 00000000 00001010 -j
	11111111 11111111 11111111 11110110 补码相加结果

	10000000 00000000 00000000 00001001
	10000000 00000000 00000000 00001010 ——源码 -10
	printf("%d\n", i + j);
	-10

	return 0;
	
}

5.第五道习题

输出什么?

#include<stdio.h>

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

	return 0;
}

答案是无限循环!为什么呢?因为从上面的转换图可以看出,无符号数是没有符号位的,当i--到0的时候,再-1 会变成全11111111 11111111 11111111 11111111 因为无符号数没有符号位全是数值位,会一直减下去 变成0 又从0开始循环 所以是一个死循环。

我们可以通过Sleep函数观察下此代码

#include<stdio.h>
#include<Windows.h>
int main()
{
	unsigned int i;
	for (i = 9; i >= 0; i--)
	{
		Sleep(1000);//毫秒
		printf("%u\n", i);
	}

	return 0;
}

6.第六道习题

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

答案是255。为什么呢?

从数学的角度来讲 代码理论应该是这样的 如下图

a[i]会从-1一直减到-1000 但实际上不是这样的。

char的范围是-128~127

当从-1减到-128时 再减去1 会变成127 126 ....到0

strlen是求字符串长度的,统计的是\0之前的出现的字符个数,而\0等同于0

-1到-128 是128个数字 127到0 一共是128个数字 但0==\0    所以是128+127=255

因此strlen只统计0之前出现的数字字符个数 因此输出结果是255。

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

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

相关文章

09 创建型模式-建造者模式

1.建造者模式介绍&#xff1a; 建造者模式 (builder pattern), 也被称为生成器模式 , 是一种创建型设计模式 定义: 将一个复杂对象的构建与表示分离&#xff0c;使得同样的构建过程可以创建不 同的表示。 2.建造者模式要解决的问题 建造者模式可以将部件和其组装过程分开&am…

SLAM从入门到精通(构建自己的slam包)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 我们学习了很多的开源包&#xff0c;比如hector、gmapping。但其实我们也可以自己编写一个slam包。这么做最大的好处&#xff0c;主要还是可以帮助…

【会议征稿通知】第三届大数据经济与数字化管理国际学术会议(BDEDM 2024)

2024 3rd International Conference on Big Data Economy and Digital Management 第三届大数据经济与数字化管理国际学术会议&#xff08;BDEDM 2024&#xff09; 第三届大数据经济与数字化管理国际学术会议&#xff08;BDEDM 2024&#xff09;将于2024年1月12-14日于宁波召…

分布式Trace:横跨几十个分布式组件的慢请求要如何排查?

目录 前言 一、问题的出现&#xff1f; 二、一体化架构中的慢请求排查如何做 三、分布式 Trace原理 四、如何来做分布式 Trace 前言 在分布式服务架构下&#xff0c;一个 Web 请求从网关流入&#xff0c;有可能会调用多个服务对请求进行处理&#xff0c;拿到最终结果。这个…

AGI热门方向:国内前五!AI智能体TARS-RPA-Agent落地,实在智能打造人手一个智能助理

早在 1950 年代&#xff0c;Alan Turing 就将「智能」的概念扩展到了人工实体&#xff0c;并提出了著名的图灵测试。这些人工智能实体通常被称为 —— 代理&#xff08;Agent&#xff09;。 代理这一概念起源于哲学&#xff0c;描述了一种拥有欲望、信念、意图以及采取行动能力…

SystemVerilog Assertions应用指南 Chapter1.33 在蕴含中使用 if/else

SVA允许在使用蕴含的属性的后续算子中使用“if/else”语句。 属性 p_if_else检査如果信号“ start”的下降沿被检测到,就是个有效开始,接着一个时钟周期后,信号“a”或者信号“b”为高。在现行算子成功匹配时,后续算子有两个可能的路径。 1.如果信号“a”为高,…

javaEE - 2(11000字详解多线程)

一&#xff1a;多线程带来的的风险-线程安全 线程安全的概念&#xff1a;如果多线程环境下代码运行的结果是符合我们预期的&#xff0c;即在单线程环境应该的结果&#xff0c;则说这个程序是线程安全的。 当多个线程同时访问共享资源时&#xff0c;就会产生线程安全的风险&am…

PHP的学习入门建议

学习入门PHP的步骤如下&#xff1a; 确定学习PHP的目的和需求&#xff0c;例如是为了开发网站还是为了与数据库交互等。学习PHP的基础语法和程序结构&#xff0c;包括变量、数据类型、循环、条件等。学习PHP的面向对象编程&#xff08;OOP&#xff09;概念和技术。学习与MySQL…

1811_spacemacs从v.0.200.13升级到v.0.200.14的几点变化感受

全部学习汇总&#xff1a; GreyZhang/editors_skills: Summary for some common editor skills I used. (github.com) 安装了全新的spacemacs的配置&#xff0c;查看了一下版本是v.0.200.14。在此之前&#xff0c;我使用的版本是v.0.200.13。现在还没有在这个配置上完成我所有的…

win32汇编-使用子程序

当程序中相同功能的一段代码用得比较频繁时&#xff0c;可以将它分离出来写成一个子程序&#xff0c;在主程序中用call指令来调用它。这样可以不用重复写相同的代码&#xff0c; 仅仅用call指令就可以完成多次同样的工作了。Win 32汇编中的子程序也采用堆栈来传递参数&#xff…

【小黑嵌入式系统第四课】嵌入式系统硬件平台(二)——I/O设备、通信设备(UARTUSB蓝牙)、其他(电源时钟复位中断)

上一课&#xff1a; 【小黑嵌入式系统第三课】嵌入式系统硬件平台&#xff08;一&#xff09;——概述、总线、存储设备&#xff08;RAM&ROM&FLASH) 文章目录 一、I/O设备1. 定时器/计数器2. ADC和DAC3. 人机接口设备3.1 键盘3.2 LCD显示器3.3 触摸屏 二、通信设备1. 通…

C#,数值计算——分类与推理Phylagglom的计算方法与源程序

1 文本格式 using System; using System.Collections.Generic; namespace Legalsoft.Truffer { public abstract class Phylagglom { public int n { get; set; } public int root { get; set; } public int fsroot { get; set; } p…

idea启动vue项目:Error:0308010C:digital envelope routines::unsupported

此问题是因为Node.js的版本原因&#xff0c;此处安装的Node.js是最新长期维护版: 18.16.0 (includes npm 9.5.1) 有两种解决办法&#xff1a; #1、方法一 重新安装低版本的node.js#2、方法二 在package.json文件中进行配置【此种方法较简单】介绍一下第二种方法&#xff1a; …

《动手学深度学习 Pytorch版》 9.4 双向循环神经网络

之前的序列学习中假设的目标是在给定观测的情况下对下一个输出进行建模&#xff0c;然而也存在需要后文预测前文的情况。 9.4.1 隐马尔可夫模型中的动态规划 数学推导太复杂了&#xff0c;略。 9.4.2 双向模型 双向循环神经网络&#xff08;bidirectional RNNs&#xff09;…

《向量数据库指南》——向量数据库是小题大作的方案?

假设大语言模型需要 10 秒钟才能生成一条结果,即需要存储的单条新记忆。那么我们获得 10 万条记忆的时间周期将为:100000 x 10 秒 = 1000000 秒——约等于 11.57 天。而即使我们用最简单的暴力算法(Numpy 的点查询),整个过程也只需要几秒钟时间,完全不值得进行优化!也就…

《Helm包管理工具篇:Helm工具概述和安装》

总结&#xff1a;整理不易&#xff0c;如果对你有帮助&#xff0c;可否点赞关注一下&#xff1f; 更多详细内容请参考&#xff1a;企业级K8s集群运维实战 一、Helm概述 Helm 是Kubernetes 的一个包管理工具&#xff0c;类似于Linux下的包管理工具如yum、apt等。可以方便的将之…

【试题025】C语言宏定义和表达式

题目&#xff1a; 若有宏定义: #define MOD(x,y) x%y 则执行以下语句后的输出结果是 ? int a13,b94: printf("%d\n",MOD(b,(a4)); 代码分析&#xff1a; #include <stdio.h> #define MOD(x,y) x%y //x和y两个形式参数进行模运算 int main() {/* 若有宏定义…

uniGUI 快速定制手机端输入界面布局

咱还是直奔主题&#xff0c;如何快速制作输入界面呢&#xff1f;如下图&#xff1a; 第一步&#xff0c;放置一个UnimFieldContainer&#xff0c;设置属性&#xff1a; AlignmentControluniAlignmentClient&#xff0c;让客户端处理对齐&#xff1b; LayoutConfig.Padding10,…

MySQL数据库——视图-介绍及基本语法(创建、查询、修改、删除、演示示例)

目录 介绍 语法 创建 查询 修改 删除 演示示例 介绍 视图&#xff08;View&#xff09;是一种虚拟存在的表。视图中的数据并不在数据库中实际存在&#xff0c;行和列数据来自定义视图的查询中使用的表&#xff08;称为基表&#xff09;&#xff0c;并且是在使用视图时动…

优雅而高效的JavaScript——?? 运算符、?. 运算符和 ?. .运算符

&#x1f974;博主&#xff1a;小猫娃来啦 &#x1f974;文章核心&#xff1a;优雅而高效的JavaScript——?? 运算符、?. 运算符和 ?. 运算符 文章目录 引言空值处理的挑战解决方案1&#xff1a;?? 运算符基本用法与 || 运算符的区别实际应用场景举例 解决方案2&#xff…