C语言——数据在内存中的存储(上)

news2024/11/19 19:34:04

数据在内存中的存储

1. 数据类型的介绍

之前已经介绍过C语言中的基本数据类型了,主要有:

  • char //字符数据类型
  • short //短整型
  • int //整形
  • long //长整型
  • long long //更长的整形
  • float //单精度浮点数
  • double //双精度浮点数

注意:C语言中是是没有字符串类型的。

类型的意义:

  1. 使用这种类型的数据所开辟的内存空间的大小。
  2. 如何看待内存空间的视角。

2. 类型的基本分类

【整形家族】

char :unsigned char signed char

short :unsigned short signed short

int :unsigned int signed int

long :unsigned long signed long

long long :unsigned long long signed long long

注意:C语言规定:sizeof(long)>=sizeof(int),所以long类型的占用的空间不能确定是4还是8。

【浮点数家族】

float

double

【构造类型】

数组类型

结构体类型

枚举类型

联合类型

【指针类型】

int *pi;

char *pc;

float* pf;

void* pv;

3. 整形在内存中的存储

由于变量的创建是需要空间的,具体使用的空间的大小是根据不同的类型而确定的。例如:

char ch = 0;这里ch变量是char类型,所以就在内存中占用了一个字节。

注意char类型一般C语言官方没有明确规定是 signed char还是 unsigned char,一般的编译器,例如VS上,char就是signed char 。但是除了char以外,其他的整形都是有明确规定的,例如:int就是signed int

那么不同的数据类型所能表示的范围是多少呢?我们可以通过以下代码来查看:

#include<stdio.h>
#include<stdlib.h>
#include <limits.h>
int main()
{
	printf("%d\n", INT_MAX);
	printf("%d\n", INT_MIN);
	return 0;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-133iB8YJ-1685460890411)(C:\Users\30539\AppData\Roaming\Typora\typora-user-images\image-20230528135439546.png)]

那么这些数据的范围大小是怎么计算出来的呢?这里以char为例:

char占用一个字节:一个字节有8个比特位,每一个比特位只能是0或1,所以char类型在内存中组合方式一共就有256种:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IXcfGFbt-1685460890412)(C:\Users\30539\AppData\Roaming\Typora\typora-user-images\image-20230528144545767.png)]

注意这里图中所有的二进制序列都是代表的是内存中的补码。由此观之,char类型的数据范围就是:-128~127。unsigned char的范围就是0~255。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6k6czyBp-1685460890412)(C:\Users\30539\AppData\Roaming\Typora\typora-user-images\image-20230528145453042.png)]

由此类推:short数据范围就是:-32768~32767。其他的整数数据类型的范围小伙伴们可以自行查阅。

3.1 整形在内存中的存储

计算机中保存整数二进制的方式主要有三种,分别是原码反码补码整形在内存中主要是以补码的形式保存。三种表示方法均有符号位和数值位两部分,符号位都是用0表示“正”,用1表示“负”,而数值位则是直接读取即可。

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

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

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

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

反码+1就得到补码。

对于整形来说数据存放内存中其实存放的是补码。使用补码可以将其符号位和数值域进行统一运算。

原因:

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

对于数据存放内存中存放的是补码我们可以通过编译器进行直接观察。例如:-1的补码是32个1,用16进制表示就是全f。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kpqakovm-1685460890413)(C:\Users\30539\AppData\Roaming\Typora\typora-user-images\image-20230528141952454.png)]

但我们把例子换成4的时候:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EHJdZDxY-1685460890413)(C:\Users\30539\AppData\Roaming\Typora\typora-user-images\image-20230528142156157.png)]

这里发现内存中是有低地址到高地址存放数据的。数据的低权值位是放在低地址处的。这里就要引出新概念了:大端字节序和小端字节序。

4. 大小端介绍

概念:

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

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

为什么有大端和小端

为什么会有大小端模式之分呢?这是因为在计算机系统中,我们是以字节为单位的,每个地址单元都对应着一个字节,一个字节为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处理器还可以由硬件来选择是大端模式 还是小端模式。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5g4vpl2R-1685460890414)(C:\Users\30539\AppData\Roaming\Typora\typora-user-images\image-20230528142934825.png)]

字节序以字节为单位讨论数据的存储的。一个char类型只占用一个字节,所以对char类型讨论大小端字节序是没有意义的,这里大小端字节序是针对占用的内存空间大于1个字节的整数数据类型的。

大小端字节序是由电脑内置部件决定的与编译器的类型无关。这里作者的电脑是以小端字节序存储的。

4.1 练习

Q1

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

Answer:

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

以下是错误的写法:

int main()//这种是错误的
{
	int a = 0x11223344;
	char b = (char)a;//无论如何b拿到的都是a的最低字节的数据 
	if (b == 0x44)
		printf("小端!\n");
	if (b == 0x11)
		printf("大端!\n");
	return 0;
}

上面这段代码无论如何b拿到的都是a的最低字节的数据

Q2

//输出什么?
#include <stdio.h>
int main()
{
    char a= -1;
    unsigned char c=-1;
    printf("a=%d,c=%d",a,c);
    return 0;
}

char a= -1;//-1的二进制补码是32个1。因为char只有一个字节,存放不下,先发生截断,a中存放的就是8个1。

unsigned char c = -1;//c也是只有1个字节,发生截断,存放的也是8个1。

printf("a = %d,c = %d",a,c);执行时,由于%d代表的是有符号打印,所以a和c均会发生整形提升:

先看a的整形提升:a是有符号数,整形提升时补的是符号位,所以就补1,变成32个1,又因为是以有符号的形式打印,而符号位又是1,所以将32个1翻译成原码就是-1,即打印-1.

再看b的整形提升,b是无符号数,整形提升时,最高位补0,所以b就变成了24个0和8个1,又因为是以有符号的形式打印,而符号位是0,翻译成原码就是255。

运行结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VnpFNqgL-1685460890414)(C:\Users\30539\AppData\Roaming\Typora\typora-user-images\image-20230528145735159.png)]

Q3

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

-128的

原码:100000000000000000000000010000000

反码:111111111111111111111111101111111

补码:111111111111111111111111110000000

由于a只有8比特的空间,所以发生截断,a中存放的是:10000000

在执行 printf("%u\n",a);语句时,因为%u是无符号整形打印:所以a会发生整形提升,由于a是signed char,为有符号类型,整形提升时高位补符号位的数,也就是补1,所以此时a中存放的是:11111111111111111111111110000000,因为是%u无符号打印,所以直接将这串二进制序列看作原码进行打印。

运行结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WN0RLHE5-1685460890414)(C:\Users\30539\AppData\Roaming\Typora\typora-user-images\image-20230528150508845.png)]

Q4

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

先写出128的补码,再截断存储到a中,a中存放的也是:10000000,与Q3类似。

运行结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ciQP3UZd-1685460890415)(C:\Users\30539\AppData\Roaming\Typora\typora-user-images\image-20230528150659634.png)]

Q5

int main()
{
	//输出什么?
	int i = -20;
	unsigned  int  j = 10;
	printf("%d\n", i + j);
	//按照补码的形式进行运算,最后格式化成为有符号整数
	return 0;
}

先写出i的

原码:10000000000000000000000000010100

反码:11111111111111111111111111101011

补码:11111111111111111111111111101100

写出j的

补码:00000000000000000000000000001010

接着让这两个补码相加:

11111111111111111111111111101100+

00000000000000000000000000001010=

11111111111111111111111111110110

又因为是%d以有符号整形进行打印,所以将相加之后的二进制的最高位看作符号位。所以这里将相加得到的二进制序列转换成原码:10000000000000000000000000001010,也就是-1。

运行结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vxadLC1q-1685460890415)(C:\Users\30539\AppData\Roaming\Typora\typora-user-images\image-20230528151358092.png)]

Q6

//输出什么?
unsigned int i;
for(i = 9; i >= 0; i--)
{
    printf("%u\n",i);
}

这里由于i的数据类型是unsigned int 类型,是恒>=0的,所以I>0这个条件会一直满足,程序发生死循环。

运行结果:发生死循环

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P9xU8IBl-1685460890415)(C:\Users\30539\AppData\Roaming\Typora\typora-user-images\image-20230528151504019.png)]

Q7

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

这里arr[i]的值一开始是-1,-2,-3……当arr[i]的值变成-128时,,由本文之前画的图可知,此时再减1就会变成127,接着就是126,125,……1,0.这里strlen计算数组的长度时,会遇到’\0’才停止,而’\0’的ascll码值就是0,即这里数组的长度就是当arr[i]变成0之前的元素的个数,这里由-1到-128,再由-128到127再到1,一共有255个元素。所以结果就是255.

运行结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wAHbsYh1-1685460890416)(C:\Users\30539\AppData\Roaming\Typora\typora-user-images\image-20230528151546746.png)]

Q8

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

此处由于i的数据类型是unsigned char 其数据范围是[0,255],始终是大于0的,所以会一直打印,发生死循环。

运行结果:发生死循环

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jTcEiukT-1685460890416)(C:\Users\30539\AppData\Roaming\Typora\typora-user-images\image-20230528151616258.png)]

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

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

相关文章

JVM学习笔记(中)

1、垃圾回收算法 标记清除法 特点&#xff1a; 速度较快会产生内存碎片 注意&#xff1a;这里的清除并不是真正意义上的清除&#xff0c;即每个字节都清0&#xff0c;而是记录一下被清除的对象的起始和结束的地址&#xff0c;当下一次分配给一个新对象时&#xff0c;新对象…

一文看懂数字化转型丨三叠云

相信很多朋友在最近这几年对“数字化”、“数字化转型”等名词听得很多了吧&#xff0c;去网上搜搜“数字化转型”&#xff0c;这几年各式各样的信息如火如荼&#xff0c;充斥着互联网平台和大大小小的企业。 一、什么是数字化转型&#xff1f; 那么“数字化”和“数字化转型”…

【ros/ros2】LCN及ros2节点的LCN改写

文章目录 序言1. ros2两种节点类型2. LCN是什么3. LCN状态转换4. LCN状态转换要做的事5. LCN节点功能划分6. ros2节点的LCN改写 序言 背景&#xff1a;ros2节点改写为lifecycle node节点 1. ros2两种节点类型 Node&#xff1a;和ros1中一样的节点基类LifecycleNode&#xff…

法规标准-ISO 11270标准解读(2014版)

ISO 11270是做什么的&#xff1f; ISO 11270全名为智能交通系统-车道保持辅助系统(LKAS)-性能要求及测试步骤&#xff0c;其中主要是对LKAS系统的功能要求、性能要求及测试步骤进行了介绍。 功能要求 LKAS应至少提供以下操作和状态转换&#xff1a; ——从LKAS off到LKAS on的…

chatgpt赋能python:Python中的精度问题

Python中的精度问题 如果你曾经在Python中处理浮点数&#xff0c;你可能会遇到精度问题。当使用不同的运算符和内置函数时&#xff0c;浮点数很容易产生舍入误差。这种误差可能会导致意想不到的结果&#xff0c;特别是在科学计算和金融应用中。 为什么会出现精度问题&#xf…

Spring AOP简介及相关案例

目录 一、Spring AOP简介 二、AOP相关术语 三、AOP入门案例 1. 引入依赖 2. 编写连接点 3. 编写通知类 4. 配置切面 5. 测试 四、通知类型 1. 编写通知方法 2. 配置切面 3. 测试 五、切点表达式 六、多切面配置 1. 编写发送邮件的通知 2. 配置切面 3. 测试 …

flex 布局的基本概念 - 详解

flex 布局的基本概念 Flexible Box 模型&#xff0c;通常被称为 flexbox&#xff0c;是一种一维的布局模型。它给 flexbox 的子元素之间提供了强大的空间分布和对齐能力。本文给出了 flexbox 的主要特性&#xff0c;更多的细节将在别的文档中探索。我们说 flexbox 是一种一维的…

Metasploit超详细安装及使用教程(图文版)

通过本篇文章&#xff0c;我们将会学习以下内容&#xff1a; 1、在Windows上安装Metasploit 2、在Linux和MacOS上安装Metasploit 3、在Kali Linux中使用 Metasploit 4、升级Kali Linux 5、使用虚拟化软件构建渗透测试实验环境 6、配置SSH连接 7、使用SSH连接Kali 8、配…

C++ vector与map的结合运用

目录 vector和map的简单介绍&#xff1a; 今天我们用vector容器和map容器实现以下简单的功能&#xff1a; 案例描述&#xff1a; 图解: ​ 实现步骤&#xff1a; 代码实现&#xff1a; 运行结果&#xff1a; vector和map的简单介绍&#xff1a; map和vector都是C STL&…

C++11 -- 包装器

文章目录 function包装器function包装器的概念function的运用function实例化使用function解决逆波兰表达式 bind包装器bind包装器相关介绍bind绑定函数固定参数 function包装器 function包装器的概念 function包装器,也叫做适配器,它的本质是一个类模板. 例如: 1 template&l…

chatgpt赋能python:Python中的迭代器

Python中的迭代器 在Python中&#xff0c;迭代器是一种对象&#xff0c;它可以让我们可以遍历&#xff08;或迭代&#xff09;序列中的元素而不必了解它们如何存储在内存中。迭代器是Python中许多高级构造的基础 - 他们节省了空间&#xff0c;并且它们能够帮助我们更有效地处理…

Redis高级篇 - 多级缓存

多级缓存 1.什么是多级缓存 传统的缓存策略一般是请求到达Tomcat后&#xff0c;先查询Redis&#xff0c;如果未命中则查询数据库&#xff0c;如图&#xff1a; 存在下面的问题&#xff1a; 请求要经过Tomcat处理&#xff0c;Tomcat的性能成为整个系统的瓶颈 Redis缓存失效时…

VUE项目运行失败原因以及解决办法(以vscode为例)

1.正常运行&#xff1a; Ctl J打开终端&#xff0c;并运行如下命令&#xff1a; npm run serve 正常情况下&#xff0c;就可以得到本地和网络链接&#xff0c;如下&#xff1a; 点击链接即可进入到编辑好的页面。 不过&#xff0c;你也可能遇到如下情况↓↓↓ 2.无法找到pac…

创建第一个.NET MAUI应用

1.打开VS2022,创建新项目,并选择.NET MAUI应用,然后点击下一步 2.输入项目相关配置,然后点击下一步 3. 选择框架版本,然后点击创建 4.项目创建成功后会自动打开概述页 5.平台框架切换 6.启动应用,如有下图提示,启动开发者模式 成功启动应用 7.修改应用 修改MainPage.xml如下图…

Baumer工业相机堡盟工业相机如何使用CameraExplorer软件设置MultiROI模式以及该模式的优势以及行业应用

Baumer工业相机堡盟工业相机如何使用MultiROI模式以及该模式的优势以及行业应用 Baumer工业相机Baumer工业相机的MultiROI模式的技术背景Baumer工业相机使用CameraExplorer软件设置MultiROI模式1.开启Baumer工业相机MultiROI模式2.关闭Baumer工业相机MultiROI模式3.测试使用Bau…

C++ 排序算法

&#x1f914;排序算法&#xff1a; &#x1f4d6;1.sort 对容器内元素进行排序 &#x1f4d6;2.random_shuffle 洗牌 指定范围内的元素随机调整次序 &#x1f4d6;3.merge 容器元素合并&#xff0c;并整合到另一个容器中 &#x1f4d6;4.reverse 反转指定容…

强推宝藏网站

最近还是有很强烈的感受&#xff0c;方法大于努力。最近就整理了一下大学期间比较好用的网站&#xff0c;也陪我度过了一段时间了&#xff0c;排名不分先后&#xff0c;把压箱底的东西拿出来了。 ChatGPT WeTab 新标签页https://www.wetab.link/ChatGPT国内免费使用方法有哪些…

【数据结构6】二叉树的基本操作

文章目录 ⭐️写在前面的话⭐️二叉树的一些基本操作1、结构定义2、先序创建这棵树3、按满二叉树方式创建4、三种递归遍历5、层次遍历6、求二叉树的深度7、求叶子结点数8、三种非递归遍历9、先序线索化二叉树10、先序线索化后遍历11、中序线索化二叉树12、中序线索化后遍历主函…

MySQL进阶- SQL优化和视图

目录 SQL优化插入数据时的SQL优化&#xff08;insert优化&#xff0c;和大批量数据插入&#xff09;主键优化order by优化&#xff08;排序操作的优化&#xff09;group by优化&#xff08;分组优化&#xff09;limit优化&#xff08;分页查询优化&#xff09; SQL优化 插入数…

[RUST/腐蚀]Windows-开服服务端下载以及配置

一、前置要求 1.SteamCMD&#xff1a;SteamCMD - Valve Developer Communityhttps://developer.valvesoftware.com/wiki/SteamCMD 2.通过SteamCMD下载RUST/腐蚀服务端。 二、SteamCMD 注意&#xff1a;所有目录均应避免出现中文。 1.建立SteamCMD文件夹&#xff0c;如 D:\st…