前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >数据在内存中的存储——整数

数据在内存中的存储——整数

作者头像
薛定谔方程难
发布2024-01-23 16:17:19
1240
发布2024-01-23 16:17:19
举报
文章被收录于专栏:我的C语言我的C语言

何以称英雄人物?识以领其先。——袁枚

1、整数的存储

任意一个整数(当然是不能超过INT_MAX的一个数字),都是以2进制的表示方式存储的,表示方法有三种,分别为原码,反码,补码 而这三种方法都是既有符号位又有数值位的两个部分,符号位都是0来表示“正”,用1来表示“负”,最高的那位被当作是符号位,剩下来的31个bit全是数值位。 正数的三种表示形式都是相同的 而负数三种表示方式不同 原码:直接将数值按照正负数的形式,表示为二进制,就是原码 反码:将原码的符号位不改变,其余的按位取反。 补码:反码+1得到。 当然不管是正数还是负数,整数的存储存放的就是补码。 关于为什么要存放补码存贮,其实真正的原因是因为,使用补码,可以将符号位和数值域统一处理,同时加法和减法也可以统一处理,并且原码和补码的相互转换的处理过程是相同的,不需要额外的硬件电路(符号位不变,取反,+1)

2、大小端字节序,字节序判断

在知道存储的方法后,那我们不经想起到底是怎么,才把这样的4个字节的数字在内存中存储的呢?难道就是我们写的1234,这样子转化为2进制后从前往后排列吗?其实,可以调试一下,既然在监视内存的时候会转化为16进制,那我们就设置一个值,整数,但是以16进制写,并且,还要能清楚方便哪是开头,哪是结尾,肯定是不能写一个全是一个数字的数吧。下面看这段!

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

可以看到这里我们查看&a的时候他却是以44,33,22,11的形式存在,并且当我们把每一排的列数都是设置为1 的话,能够看见地址是从上向下的,也就是说,在数字低位置的情况下(个位是最低的),存在了低地址的地方。

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

在这篇文章中,提到在VS2019环境下,使用出现了死循环,并且还介绍了使用的习惯和内存使用顺序可以看一下,了解了解,但是还是不相同的,千万别搞错了,这里是以一个一个数为存储来说。 当然要记住,下面的这些重要的东西。

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

==确实是在使用栈的时候是从高地址到低地址,但是打印出来的数值不一定从小到大,反而真正的情况还可能是向上面的图一样,高地址的打印数值小于低地址的。==也相当于,对于栈来说,是从上到下增长,从高地址到低地址,但是可能对于整个而言,有可能是从数值低的到高的。

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

这两幅图片是很重要的,要多加理解。

2、1大小端是什么?

其实,就像刚刚提到的一样,无论是什么,只要是超过一个字节的内存存储,一定是会有顺序问题的存在。 那么首先先介绍一下大小端是什么。 ==大端:==是指数据的低位字节内容保存在内存中的高地址处,而数据高字节内容,保存在内存中的低地址处。 ==小端:==是指数据的低位字节内容保存在内存中的低地址处,而数据高字节内容,保存在内存中的高地址处。 记住大小端的区别,方便区分!不同的编译器上面可能有不同的,在我使用的Visual Stdio上面是小端。

2、2大小端存在原因

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

2、3模拟实现函数判断大小端

其实判断函数大小端也是比较容易的,因为当我们int a=1的时候,这时,a在内存中的存储,只有一个字节是有意义的数值,其他都是0,所以我们可以根据这点来写程序。

代码语言:javascript
复制
int check_sys()
{
	int i = 1;
	return (*(char*)&i);
}
int main()
{
	int ret = check_sys();
	if (ret == 1)
	{
		printf("xiao端\n");
	}
	else
	{
		printf("da端\n");
	}
	return 0;
}

这样的,可以确定机器本身到底是怎么来存储的。 ==注意:==这地方的这段代码一定要了解清楚,不能马虎。

代码语言:javascript
复制
	return (*(char*)&i)
	return(char*)a

这两段代码是不一样的,并且能表示出大小端的只有第一个方法,第二个是不可以的。第二个的意思其实就是,直接把a转化为char,但是直接转化的话,无论是什么,他只会变为char类型的1,无法判断,到底是大端还是小端。 当然除了上面的方法之外,还有运用联合体的方法来辨别到底是大端还是小端。使用这段代码的情况下,不仅能很好的理解联合体,还能更深刻的理解什么是大小端并且知道联合体是怎么储存的。

代码语言:javascript
复制
int check_sys()
{
	union
    {
		int i;
		char c;
	}un;
	un.i = 1;
	return un.c;
}

3、关于数据存储的问题

3、1整型提升,类别存储大小改变

代码语言:javascript
复制
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;
}
在这里插入图片描述
在这里插入图片描述

对于a来说,原本存贮的数字应该是10000000 00000000 00000000 00000001 然后因为是负数所以要取反11111111 11111111 11111111 11111110 最后一步,是加上1,的到最后的补码11111111 11111111 11111111 11111111 但是由于a的存储是char类型的,所以要发生截断,最后存储的时候却是11111111 相对于b来说步骤是一模一样的。 对于c来说步骤也是一样的。 类型在存储的时候没有什么用,只是存储的时候要多大的空间,只有当用的时候,才会有所不同。 所以这里要关注,要关注一下整型提升的问题%d - 是以十进制的形式打印有符号的整数 整型提升: 1、当是无符号的时候,提升的时候,高位补0。 2、当是有符号的时候,提升的时候,是按照最高位置的补,0的话就补上0,1的话就补上1。 所以,对于a来说,a在整型提升的时候,会变为32位都是-1的数,那么也就是-1。对于b来说也是相同的道理,因为signed char就是等于char。**但是对于c来说,是无符号的,提升的时候要按照前位补0的方法,所以到最后c的值变为了255。**当然要记住这时候还是补码,需要取反加1才会变为原码。

3、2不同类型的取值范围造成的差别

就以char类型的取值范围来举例。以此类推,可以将各个的类型都能推断出来。 char类型是1个字节,8个比特位 ** 00000000=1 00000001=2 00000010=3 ····· 10000000=-128(此时取反+1,但是如果这样子的话会发现,会溢出,所以就是规定了此时就是-128) 10000001=-127 10000010=-126 ····· 111111110=-2 111111111=-1(要取反后+1) **

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

所以,对于上面写的一段来说,其实可以画图来简单的表示一下。

4、练习

4、1练习1(关于整型提升)

代码语言:javascript
复制
#include<stdio.h>
int main()
{
	char a = 128;
	printf("%u\n", a);
	return 0;
}

注意:%u是按照无符号的形式打印。

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

关于此时的a的原码是10000000 00000000 00000000 10000000 反码是11111111 11111111 11111111 01111111 补码是11111111 11111111 11111111 1000000 然后还要发生截断,所以最后a存储的只有10000000(12) 但是打印的时候是%u,发生整型提升,又是因为a是char所以按照有符号位的整型提升,高位全部补上1,但是最后又是%u打印无符号整型,所以就相当于打印一个数子的二进制为11111111 11111111 11111111 10000000

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

这下结果就是对的了。 所以,整型提升的时候,是看a到底是有没有符号的,无论%d还是%u的打印方式,整型提升看的是数字的类型本身,只有打印的时候,才会在意打印的形式。

4、2练习2(关于整型提升)

代码语言:javascript
复制
int main()
{
	char a = 128;
	printf("%u\n", a);
	return 0;
}
在这里插入图片描述
在这里插入图片描述

为什么char a=128的时候还是原来a=-128的数字呢? 128时,a会发生截断,因为char类型放不下在,只能放下00000000 00000000 00000000 10000000,截断之后存放的时10000000却和上面的练习1中的char a=-128相同,存储的相同,所以最后结果也应该相同。

4、3练习3(关于0是\0)

代码语言:javascript
复制
int main()
{
	char a[1000] = { 0 };
	int i;
	for (i = 0; i < 1000; i++)
	{
		a[i] = -1 - i;
	}
	printf("%d", strlen(a));
	return 0;
}
在这里插入图片描述
在这里插入图片描述

啊啊啊啊?为啥这里又是255呢?怎么看也不应该是255吧,因为a是可以存1000个呢,怎么可能就255个啊。但是我们要注意到strlen的用法是什么,strlen在使用的时候,会注意到,当遇到\0的时候就会停止(计算\0之前的数字有多少)。但是数组里面也没有\0啊,但是\0的ASCII码确是0 从开始应该是怎么存储?-1,-2,-3,-4···-128,127,126,125···,2,1,0

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

正好0就是存放’\0’的,也可以通过调试看到有255个数是在\0之前的。 所以答案才会使255个。

4、4练习4(关于死循环)

代码语言:javascript
复制
int main()
{
	int i = 0;
	unsigned char a = 0;
	for (i = 0; i <= 255; i++)
	{
		printf("haha\n");
	}
	return 0;
}

此时就是死循环,因为unsigned char类型是不会比255数字还大存在的,所以判断条件是不会停止的,所以就会一直循环,不停止的打印。

还有剩下来的浮点数在内存中的存储,下章解释清楚

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2023-10-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1、整数的存储
  • 2、大小端字节序,字节序判断
    • 2、1大小端是什么?
      • 2、2大小端存在原因
        • 2、3模拟实现函数判断大小端
        • 3、关于数据存储的问题
          • 3、1整型提升,类别存储大小改变
            • 3、2不同类型的取值范围造成的差别
            • 4、练习
              • 4、1练习1(关于整型提升)
                • 4、2练习2(关于整型提升)
                  • 4、3练习3(关于0是\0)
                    • 4、4练习4(关于死循环)
                    相关产品与服务
                    对象存储
                    对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
                    领券
                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档