C语言之数据在内存中的存储(1),整形与大小端字节序


目录

前言

一、整形数据在内存中的存储

二、大小端字节序

三、大小端字节序的判断

四、字符型数据在内存中的存储

总结



前言

     本文主要讲述整型包括字符型是如何在内存中存储的,涉及到大小端字节序这一概念,还有如何判断大小端,希望对大家有所帮助


❤️感谢支持,点赞关注不迷路❤️


(本文内容涉及到整形提升,如不了解,主页中可查看详细,两篇结合起来看更深入)

一、整形数据在内存中的存储

我们都知道。整数的2进制表示有3种,即原码、反码、补码

     

有符号的整数,三种表示方法均有符号位和数值位两部分,符号位都是用0表示“正”,用1表 示“负”,最高位的一位是被当做符号位,剩余的都是数值位。

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

正整数的原、反、补码都相同。

负整数的三种表示方法各不相同。

对于整形来说:数据存放内存中其实存放的是补码。

为什么呢?

     

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

(主页·位操作符有详细举例)


二、大小端字节序

运行以下代码,在vs(32位)调试窗口观察其在内存中的存储情况

#include <stdio.h>int main()
{int a = 0x11223344;return 0;
}

内存调试窗口:

(0x0058FB3C是a在内存中的首地址)

发现:a = 0x11223344,它在内存中存储的却是 44 33 22 11,是倒着存储的。相信我们平时调试的时候肯定会有这样的疑问,这就涉及到了大小端字节序了。

什么是大小端?

其实超过一个字节的数据在内存中存储的时候,就有存储顺序的问题,按照不同的存储顺序,我们分为大端字节序存储和小端字节序存储,下面是具体的概念:

  • 大端(存储)模式: 是指数据的低位字节内容保存在内存的高地址处,而数据的高位字节内容,保存在内存的低地址处。
  • 小端(存储)模式: 是指数据的低位字节内容保存在内存的低地址处,而数据的高位字节内容,保存在内存的高地址处。

解释说明:例如上面的 a = 0x11223344,我们按照数学方式读这个数时,44是不是个位和十位。它相比于前面的 112233 是不是算低位。44 就是一个低位字节内容,VS是以小端模式存储数据的,所以 44 就存储在内存的低地址处,其余的就按顺序往高处存储,这样我们看到的就是 44 33 22 11了。那么假如我们用的是大端模式存储数据的,那么我们看到的就是 11 22 33 44 ,11在内存中对应的是低地址,44 是高地址。

为什么有大小端?

为什么会有大小端模式之分呢?

    

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

     

例如:⼀个 16bit 的 short 型 x ,在内存中的地址为 0x0010,x 的值为 0x1122 ,那么0x11为高字节,0x22为低字节。对于大端模式,就将0x11放在低地址处,即0x0010中,0x22放在高地址处,即0x0011中。小端模式,刚好相反。我们常用的X86 结构是小端模式,而 KEIL C51 则为大端模式。很多的ARM,DSP都为小端模式。有些ARM处理器还可以由硬件来选择是大端模式还是小端模式。


三、大小端字节序的判断

判断大小端其实很简单,定义 int a = 1,16进制为 0x 00 00 00 01,我们只需要访问其首地址对应的字节内容即可,小端会以 0x 01 00 00 00 从低地址往高地址排放,大端会以 0x 00 00 00 01 从低地址往高地址排放。

如下,一小段代码即可:

#include <stdio.h>int check_sys()
{int a = 1;return *(char*)(&a);
}int main()
{int ret = check_sys();if (ret == 1){printf("小端\n");}else if (ret == 0){printf("大端\n");}return 0;
}

运行结果(VS):


四、字符型数据在内存中的存储

字符型其实也属于整形范畴,存储的是其ASCII码值。

我们可以先观察以下代码:

#include <stdio.h>int main()
{char a = -1;signed char b = -1;unsigned char c = -1;printf("a=%d, b=%d, c=%d\n", a, b, c);return 0;
}

运行结果:

解疑:

  1. 首先我们知道,字符型变量以%d打印时是要发生整形提升的。
  2. 然后a为char类型,vs中,char类型默认为有符号字符型,也就是等同于 signed char,所以a整形提升是要看符号位的,-1的补码是11111111 11111111 11111111 11111111,a大小只有一个字节,存储时会发生截断取后8位,a其补码为11111111,整形提升后 11111111 11111111 11111111 11111111,以%d打印的是原码,再转为原码为10000000 00000000 00000000 00000001,因此打印的还是-1
  3. b与a一样,然后就是c,c是无符号字符形,c的补码还是1111111,整形提升,无符号整形提升高位补0,也就是 00000000 00000000 00000000 11111111,再转为原码,因为是无符号整形,原反补相同,所以原码还是 00000000 00000000 00000000 11111111,打印出来就是255。


在看这段代码:

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

运行结果:

解疑:

  1. 我们发现以%u(无符号整形)方式打印时,打印出的是一个非常大的数字,以%d打印还是原数值,那么为什么会这样呢
  2. 首先,a以%u打印时,还是会发生整形提升,-128有点大,我们先算出-128的原码为  10000000 00000000 00000000 10000000,取反加1算出-128的补码为 11111111 11111111 11111111 10000000,截断后 a 的补码就是10000000,以%u打印,虽然是以无符号整形打印,但是整形提升时是根据原类型进行提升的,原类型为char,有符号字符型,所以高位补符号位1,即 11111111 11111111 11111111 10000000。然后%u就发挥作用了,要把整形提升后的这个补码看成无符号整形,这时候原反补就相同,原码就是 1111111 11111111 11111111 10000000,打印出来的结果就是上图中很大的数字,我们可以借助计算器验证:
  3. 以%d打印时接着2中整形提升后的补码 11111111 11111111 11111111 10000000,这里是以%d打印,所以要看成有符号的整形,其原码就要进行取反加1,即 10000000 00000000 00000000 10000000,打印出来就是-128


再看这段代码:

#include <stdio.h>int main()
{char a = 128;printf("%u\n", a);printf("%d\n", a);return 0;
}

运行结果:

解疑:

  1. 我们发现128的结果与-128的结果相同,这又是为什么
  2. 其实我们自己再重新算一下就会发现,a存储时补码都是 10000000,因为128与-128的补码后8位是完全相同的,截断时值就相同。算一个特殊情况,因为a的值相同,所以后续以%u或者%d打印时效果也相同。


通过以上例题,我们可以再推导一下char型变量(有符号)的存储范围为啥是 -128~127了,还有 unsigned char 为啥是 0~255

我们画图分析:

如此,我们可以画成一个圆:

这就是 char 类型为什么存储范围是-128~127,哪怕赋值的数字超过这个范围,也会被截断在这个范围内。

这就是unsigned char 存储范围为什么是0~255。

其实,signed short 和 unsigned short 类型数据也可以画圆圈表示,int也可以,这里如果感兴趣可以自己画着试试。同上即可


 

总结

        以上就是本文的全部内容,希望对你有所帮助。

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

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

相关文章

2、需求工具 - 编程实战工具系列文章

需求分析工具 笔者对于需求分析工具的应用&#xff0c;主要是思维导图软件和Word。思维导图用来理清需要的需求功能&#xff0c;而Word用来记录每个需求功能的实际内容。 对于思维导图软件&#xff0c;笔者用过几个&#xff0c;但是有些需要注册码&#xff0c;有些需要费用&…

谷粒商城学习-09-配置Docker阿里云镜像加速及各种docker问题记录

文章目录 一&#xff0c;配置Docker阿里云镜像加速二&#xff0c;Docker安装过程中的几个问题1&#xff0c;安装报错&#xff1a;Could not resolve host: mirrorlist.centos.org; Unknown error1.1 检测虚拟机网络1.2 重设yum源 2&#xff0c;报错&#xff1a;Could not fetch…

Redis基础教程(十五):Redis GEO地理信息查询与管理

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; &#x1f49d;&#x1f49…

绝区伍--2024年AI发展路线图

2024 年将是人工智能具有里程碑意义的一年。随着新模式、融资轮次和进步以惊人的速度出现&#xff0c;很难跟上人工智能世界发生的一切。让我们深入了解 2024 年可能定义人工智能的关键事件、产品发布、研究突破和趋势。 2024 年第一季度 2024 年第一季度将推出一些主要车型并…

【React Hooks原理 - useEffect、useLayoutEffect】

介绍 在实际React Hooks项目中&#xff0c;我们需要在项目的不同阶段进行一些处理&#xff0c;比如在页面渲染之前进行dom操作、数据获取、第三方加载等。在Class Component中存在很多生命周期能让我们完成这个操作&#xff0c;但是在React Hooks没有所谓的生命周期&#xff0…

python - 类和对象

一.类 类名用大写字母开头 属性是类中的变量&#xff0c;方法是类中的函数 类、class关键字&#xff1a; >>> class Turtle: ... color green ... weight 10 ... legs 4 ... shell True ... mount 大嘴 ... def climb(self): ... …

从零开始读RocketMq源码(二)Message的发送详解

目录 前言 准备 消息发送方式 深入源码 消息发送模式 选择发送方式 同步发送消息 校验消息体 获取Topic订阅信息 高级特性-消息重投 选择消息队列-负载均衡 装载消息体发送消息 压缩消息内容 构造发送message的请求的Header 更新broker故障信息 异步发送消息 …

Open3D KDtree的建立与使用

目录 一、概述 1.1kd树原理 1.2kd树搜索原理 1.3kd树构建示例 二、常见的领域搜索方式 2.1K近邻搜索&#xff08;K-Nearest Neighbors, KNN Search&#xff09; 2.2半径搜索&#xff08;Radius Search&#xff09; 2.3混合搜索&#xff08;Hybrid Search&#xff09; …

ai native 模型微调

AI native 模型微调&#xff08;fine-tuning&#xff09;是指在预训练模型的基础上&#xff0c;通过对其参数进行进一步训练&#xff0c;使其在特定任务上表现更佳。以下是关于模型微调的一些基本步骤和概念&#xff1a; ### 1. 准备数据集 - **数据收集**&#xff1a;收集适用…

后端之路——登录校验前言(Cookie\ Session\ JWT令牌)

前言&#xff1a;Servlet 【登录校验】这个功能技术的基础是【会话技术】&#xff0c;那么在讲【会话技术】的时候必然要谈到【Cookie】和【Session】这两个东西&#xff0c;那么在这之前必须要先讲一下一个很重要但是很多人都会忽略的一个知识点&#xff1a;【Servlet】 什么是…

Oracle PL/SQL 循环批量执行存储过程

1. 查询存储过程 根据数据字典USER_OBJECTS查询出所有存储过程。 2. 动态拼接字符串&#xff08;参数等&#xff09; 根据数据字典USER_ARGUMENTS动态拼接参数。 3. 动态执行 利用EXECUTE IMMEDIATE动态执行无名块。 4. 输出执行信息 利用DBMS_OUTPUT.PUT_LINE输出执行成功与…

Android Gradle 开发与应用 (十): Gradle 脚本最佳实践

目录 1. 使用Gradle Kotlin DSL 1.1 什么是Gradle Kotlin DSL 1.2 迁移到Kotlin DSL 1.3 优势分析 2. 优化依赖管理 2.1 使用依赖版本管理文件 2.2 使用依赖分组 3. 合理使用Gradle插件 3.1 官方插件和自定义插件 3.2 插件管理的最佳实践 4. 任务配置优化 4.1 使用…

Oracle 19c 统一审计表清理

zabbix 收到SYSAUX表空间告警超过90%告警&#xff0c;最后面给出的清理方法只适合ORACLE 统一审计表的清理&#xff0c;传统审计表的清理SYS.AUD$不适合&#xff0c;请注意。 SQL> Col tablespace_name for a30 Col used_pct for a10 Set line 120 pages 120 select total.…

STM32实战篇:闪灯 × 流水灯 × 蜂鸣器

IO引脚初始化 即开展某项活动之前所做的准备工作&#xff0c;对于一个IO引脚来说&#xff0c;在使用它之前必须要做一些参数配置&#xff08;例如&#xff1a;选择工作模式、速率&#xff09;的工作&#xff08;即IO引脚的初始化&#xff09;。 IO引脚初始化流程 1、使能IO引…

LED灯的呼吸功能

"呼吸功能"通常是指 LED 灯的一种工作模式&#xff0c;它模拟人类的呼吸节奏&#xff0c;即 LED 灯的亮度会周期性地逐渐增强然后逐渐减弱&#xff0c;给人一种 LED 在"呼吸"的感觉。这种效果通常用于指示设备的状态或者简单地作为装饰效果。&#xff08;就…

Spring Boot Security自定义AuthenticationProvider

以下是一个简单的示例&#xff0c;展示如何使用AuthenticationProvider自定义身份验证。首先&#xff0c;创建一个继承自标准AuthenticationProvider的类&#xff0c;并实现authenticate方法。 import com.kamier.security.web.service.MyUser; import org.springframework.se…

【Adobe】Photoshop图层的使用

Adobe Photoshop(简称PS)中的图层是图像处理中一个核心概念,它允许用户以堆叠的方式组织图像的不同部分,从而实现对图像的复杂编辑和处理而不影响原始图像。以下是关于Adobe Photoshop图层的详细介绍: 一、图层的定义 图层就像是透明的纸张,你可以在上面绘制、添加图像…

YOLOv10改进 | EIoU、SIoU、WIoU、DIoU、FocusIoU等二十余种损失函数

一、本文介绍 这篇文章介绍了YOLOv10的重大改进&#xff0c;特别是在损失函数方面的创新。它不仅包括了多种IoU损失函数的改进和变体&#xff0c;如SIoU、WIoU、GIoU、DIoU、EIOU、CIoU&#xff0c;还融合了“Focus”思想&#xff0c;创造了一系列新的损失函数。这些组合形式的…

Android Init Language自学笔记

Android Init Language由五个元素组成&#xff1a;Acttions、Commands、Services、Options和Imports。 Actions和Services隐式声明了一个新的section。所以的Commands和Options都属于最近声明的section。 Services具有唯一的名称&#xff0c;如果重名会报错。 Actions Acti…

解决Spring Boot中的高可用性设计

解决Spring Boot中的高可用性设计 大家好&#xff0c;我是微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01; 1. 高可用性设计概述 1.1 什么是高可用性&#xff1f; 高可用性指系统在面对各种故障和异常情况时&#xff0c;仍…