掌握C语言内存布局:数据存储的智慧之旅

ops/2025/1/17 14:59:09/

大家好,这里是小编的博客频道
小编的博客:就爱学编程

很高兴在CSDN这个大家庭与大家相识,希望能在这里与大家共同进步,共同收获更好的自己!!!

目录

  • 引言
  • 正文
    • 一、数据类型介绍
      • 1.内置类型
      • 2.自定义类型
      • 3.指针类型
      • 4.空类型(void)
    • 二、数据在内存中的存储规则
      • 1.整型数据在内存中的存储
        • 原码 、反码 、补码
      • 2.整型数据的运算与打印
        • (1)占位符
        • (2)数据范围
        • (3)整型提升
        • (4)数据截断
    • 三、大小端介绍
      • 1.来历
      • 2.模式介绍及效果
      • 3.模式判断
  • 快乐的时光总是短暂,咱们下篇博文再见啦!!!不要忘了,给小编点点赞和收藏支持一下,在此非常感谢!!!

引言

本文主要讲述了数据在内存中的存储位置,存储方法以及如何读取内存中的数据。如果你对其有困惑,不妨好好阅读,也许会有新的体会和感悟。
在这里插入图片描述


那接下来就让我们开始遨游在知识的海洋!

正文


一、数据类型介绍

1.内置类型

整型家族

家族成员
int
char
unsigned.int
short.int
long.long
unsigned.short
signed.short
unsigned.long.long
signed.long.long
signed.int
unsigned.char
signed.char

疑惑点:为什么char类型属于整型家族?
解释:这是因为char类型的数据在存储的时候,是以它的ASCLL值进行存储的,本质是一个整型。

浮点型家族

家族成员
float
double

注意:浮点型都是有符号的。


2.自定义类型

组成
数组
结构体.struct
枚举.enum
联合体.union

3.指针类型

组成
一级指针
用来存放内置数据类型变量的地址
二级指针
用来存放一级指针变量的地址

4.空类型(void)

常用于表示函数不需要参数
例:

#include<stdio.h>
int main(void) {printf("hello,world!");return 0;
}

运行结果:
在这里插入图片描述

当然,这常适用于非主函数


二、数据在内存中的存储规则

符号位:对于一个有符号的的数来说,最高位就是符号位,c语言中规定1表示负,0表示正。

1.整型数据在内存中的存储

原码 、反码 、补码

原码
把一个十进制数直接转化为它的二进制,就是这个数的原码。

反码
正数:还是原码
负数:符号位不变,其他位按位取反

补码
正数:还是原码
负数:反码+1

signed.int
负数.十进制
原码.二进制
符号位不变,其他位按位取反
反码
加1
补码
正数.十进制

我们要注意:

  • 对于有符号整型数据:只有屏幕上打印的是原码,而在内存中进行存储和表示的都是补码。

为什么?

序号原因
1.使用补码,可以将符号位和数值域统一处理
2.加减法也可以统一用加法处理(CPU只有加法器)

凭什么这么说?我们可以用一个简单的例子佐证我们的说法
例:

  • 写一个代码打印-1+1的结果

代码:

#include<stdio.h>
int main() {printf("%d", -1 + 1);return 0;
}

运行结果:
在这里插入图片描述

思考讨论
到这里,你可能会想:这不就理应如此吗?但事实上,这是我们c语言设计好的用补码进行运算的结果。不信?你看:如果我们用原码进行计算,我们会惊奇的发现发现:最后的打印结果应为-2。但是这就与常理违背了,-1 + 1 == -2 , 离之大谱!

所以通过这个例子,我们就不难发现:

  • 对于整型数据来说,只有屏幕上打印的是原码,而在内存中进行存储和表示的都是补码的智慧和原因
序号注意
1.原码,反码,补码的概念主要针对有符号的整型家族(signed int或int)类型的数据,而对于其他类型的数据,有其相应的存储方式
2.特殊地:我们把无符号的整型家族可以看做正数(有符号的数)进行处理,只不过这个正数没有符号位

2.整型数据的运算与打印

但是,掌握了以上的知识点之后,我们其实还并不能完全预见和理解整型数据运算屏幕上打印的结果,还得掌握一些占位符的作用,整型提升数据截断数据范围

(1)占位符
占位符作用
%d打印有符号整型数据
%u、%zd打印无符号整型数据

例:

#include<stdio.h>
int main() {int a = 2147483647;int b = 1;printf("%d %u", a + b, a + b);return 0;
}

运行结果:
在这里插入图片描述

思考:为什么同样是a + b,打印的结果确一正一负呢?

  • 这里其实涉及了后文要讲的数据范围,但我们通过这个例子依旧是能够感受到占位符不同所带来的不同打印结果,该题我们在后文再进一步剖析。

(2)数据范围

我们知道:

1.每个数据类型都有其对应的字节长度,1字节 == 8bit 位

详见:

数据类型字节
char1
short(int)2
(long) int4
float4
long long8
double8

那这些bit位是用来干嘛的呢?

2.实际上,这些bit位就是用来存放数字1或0来表示数据大小的。由此我们可以想到:那既然表示数据大小的位数是有限的,那每个类型的数据大小也应该是在一个范围内的。

详见:

数据类型范围
char-128 ~ 127, 0 ~ 255
short(int)- 2^15 ~ 2^15 - 1, 0 ~ 2^16 - 1
(long) int- 2^31 ~ 2^31 - 1, 0 ~ 2^32 - 1

注:有负号的为有符号数据类型数据的范围


那如果一个数据超过了其范围,怎么计算?

我们可以用圆环的思想去考虑:

  • 每一个无符号的数据类型从大到小都是bit位全0–>bit位全1,而到了最大的时候,也就是bit位全1,再加1就会发生数据越位,所有bit位变成全0,然后再由bit全0–>bit位全1,构成了一个头尾相交的圆环。

以unsigned char为例:

11111100
11111111
00000000

等价于:

12
225
0

而每一个符号的数据类型从大到小都是bit位全0–>bit位除了首位全1,而到了最大的时候,也就是bit位除了首位全1,再加1就会变成bit位除了首位全0,然后再变成bit位全1,再加1就会发生数据越位,所有bit位变成全0,然后再由bit全0–>bit位全1,构成了一个头尾相交的圆环。

但是和无符号数据类型不同的是:

  • 有符号的数它的二进制位比无符号的数少了一位数值位,并且我们规定1000…0000000表示的是最小的负数

但我们应注意:

  • 二进制数存储和表示无论是正数还是负数都是采用补码的方式,而只是因为我们把无符号数据看做正数,原码和补码一样,且多了一位数值位才导致了表示和存储方式看起来不同,实际都是一样用补码的形式进行存储和表示。
无符号和有符号数的异同详述
不同无符号数多了一位数值位
相同实际都是一样用补码的形式进行存储和表示
重要规定(有符号的数)1000…0000000表示的是最小的负数

以signed char为例:

10000000
11111111
00000000
01111111

等价于:

127
0
-128
-1

了解这些,我们就可以把上面的例子剖析
例子:

#include<stdio.h>
int main() {int a = 2147483647;int b = 1;printf("%d %u", a + b, a + b);return 0;
}

运行结果:
在这里插入图片描述

疑惑剖析
为什么用占位符%d打印的结果是负数?(1)十进制:2147483648–>二进制(补码):01111111111111111111111111111111;十进制:1–>二进制(补码):00000000000000000000000000000001;相加得:10000000000000000000000000000000(补码)(2)又用%d进行打印,%d是用来打印int类型数据的,所以我们要把相加得的结果转换为原码打印出来(这里要用重要规定)
为什么用占位符%u打印的结果是正数?(1)同上;(2)又用%u进行打印,%u是用来打印无符号整数的,无符号整数的特别之处——就在于我们是把它看作有符号整数中的正数,就导致:它的原码和反码是一样的,所以打印在屏幕上的数就是相加结果的十进制数
对于占位符的思考占位符给了我们一个理解和读取二进制数(补码)的视角方式

(3)整型提升
问题答案
什么叫整型提升?c语言整型算术运算总是至少缺省(默认)整型类型的精度进行计算。为了获得这个精度,表达式中字符型(char)和短整型(short)在使用之前就被系统自动转换为普通整型(int),这种转换就叫整型提升
整型提升的意义是什么?表达式的整型运算要在CPU的加法器和其他运算器件中进行,而该运算器的操作数的字节长度就为int的字节长度
数据提升的条件和对象发生在小于整型类型的数据类型上

提升规则

对象
有符号数
正数
高位补0
无符号数
看补码
最高位为1则高位补1.否则补0
负数
高位补1

本质就是看补码的最高位是1还是0,是1补1,是0补0


(4)数据截断
问题答案
什么是数据截断?就是一个字节长度较小的数据类型在接收一个字节长度较大的数据类型时只会从字节长度较大的数据的数据低位开始接收,直至填满自己的所有二进制位
会有什么结果?可能会导致数据的丢失,一定会导致字节的减少

熟练掌握这些,我们就基本能预见和理解整型数据运算屏幕上打印的结果,下面就以一些例子来加深和巩固对以上知识的理解

例:

#include<stdio.h>
int main() {char a = 3;//00000011char b = 127;//01111111char c = a + b;//因a和b参加运算,所以要发生整型提升//整型提升规则:高位补符号位(在大部分编译器中,char等价于signed char,即有符号,最高位为符号位,范围为:-128~127;无符号的char类型数据范围为:0~255;//整型提升://00000000000000000000000000000011//00000000000000000000000001111111
//结果00000000000000000000000010000000//截断(发生在高字节类型转化为低字节)://10000000              补码//10000011printf("%d", c);//打印结果为:-126return 0;
}
//也可以用圆环秒解:0 1 2 ~ 126 127 ~-1 -2 -3 ~ -127 -128 ~ 0 1 2 ~~
  • 注释的解析很全,不用再过多解释,对照上面的知识点进行理解

三、大小端介绍

1.来历

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


2.模式介绍及效果

模式(全称)效果
大端字节序存储数据的低位字节存放在高地址
小端字节序存储数据的低位字节存放在低地址

实际效果:

自己模拟
在这里插入图片描述


VS2022上的内存窗口
在这里插入图片描述


3.模式判断

我们可以借助一个2015年百度的笔试题观察大小端的区别

题目:写一个代码,实现大小端的判断

代码:

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

思路:

序号步骤
1用char*类型的指针一个一个字节地访问int类型变量a的各个字节
2我们可以把a赋值为只有第一个字节有值的整数,这里赋值为1
3前提:我们知道指针访问内存的习惯是从低地址到高地址
4如果*p == 1,说明低地址存放的字节是低位字节,也就是小端存储
5如果*p == 0,说明低地址存放的字节是高位字节,也就是大端存储

快乐的时光总是短暂,咱们下篇博文再见啦!!!不要忘了,给小编点点赞和收藏支持一下,在此非常感谢!!!


http://www.ppmy.cn/ops/150832.html

相关文章

next-auth v5 结合 Prisma 实现登录与会话管理

1. 安装依赖 npm install next-auth prisma prisma/client2. 配置 Prisma 模型 在 prisma/schema.prisma 文件中定义 User 和 Account 模型&#xff08;next-auth v5 推荐使用自定义模型&#xff09;。 model User {id String id default(uuid())email …

css hover样式调试

调试 hover后才出现的元素如何调试 打开开发者工具&#xff0c;鼠标放在hover时才出现的元素上&#xff0c;然后点击右键不要选中任何选项&#xff0c;将鼠标移动到开发者工具的调试面板中按下N键&#xff0c;此时悬浮的元素不会消失&#xff0c;定位成功 调试元素悬浮样式 …

[答疑]用例规约:系统请求3dsMax创建体块

DDD领域驱动设计批评文集 做强化自测题获得“软件方法建模师”称号 《软件方法》各章合集 第五元素 2024-12-22 12:39 潘老师&#xff0c;请教一个问题&#xff1a;在需求工作流书写系统用例规约时&#xff0c;这个“计算”&#xff08;改变&#xff09;可不可以写多个内容&…

vue用户点进详情页再返回列表页,停留在原位置

在 Vue 中使用 <keep-alive> 来缓存组件确实可以提高用户体验&#xff0c;尤其是在移动应用中。然而&#xff0c;有时候你可能希望某些页面在每次访问时都重新加载&#xff0c;而不是使用缓存的版本。为了实现这一点&#xff0c;你可以结合 key 属性和 Vue Router 的导航…

【React学习笔记】第一章:React入门

1.React介绍 中文官网&#xff1a; https://react.docschina.org/ 英文官网&#xff1a;https://reactjs.org/ 1.1 React是什么 react是一个用于构建用户界面的开源JavaScript库。&#xff08;操作DOM呈现页面&#xff09; 由Facebook的软件工程师 Jordan Walke创建&#xf…

基于微信小程序的中国各地美食推荐平台的设计与实现springboot+论文源码调试讲解

第4章 系统设计 4.1 系统设计的原则 在系统设计过程中&#xff0c;也需要遵循相应的设计原则&#xff0c;这些设计原则可以帮助设计者在短时间内设计出符合设计规范的设计方案。设计原则主要有可靠性&#xff0c;安全性&#xff0c;可定制化&#xff0c;可扩展性&#xff0c;可…

【Kotlin】上手学习之类型篇

一、类型 1.1 基本类型 主要分为 数字及其无符号版布尔字符字符串数组 1.1.1 数字 整数类型 Kotlin 提供了一组表示数字的内置类型。 对于整数&#xff0c;有四种不同大小的类型&#xff0c;因此值的范围也不同&#xff1a; 类型大小&#xff08;比特数&#xff09;最小…

2006-2020年各省人均水资源量数据

2006-2020年各省人均水资源量数据 1、时间&#xff1a;2006-2020年 2、来源&#xff1a;国家统计局、统计年鉴 3、指标&#xff1a;行政区划代码、地区名称、年份、人均水资源量 4、范围&#xff1a;31省 5、指标解释&#xff1a;人均水资源量是指一个国家或地区在一定时期…