内存存储 | 整形在内存中怎么存储呢 | 原码反码补码 | 大小端

news/2025/2/13 23:58:17/

 整型在内存中的存储

整型在二进制中的表示形式有3种:原码、反码、补码。

  • 正的整数:原码、反码、补码相同
  • 负的整数:原码、反码、补码要进行计算的

整数在内存中存储的是补码的二进制序列。

其中对于有符号整形来说,二进制中最高位是符号位,最高位为“0”表示正数,最高位为“1”表示负数。

那么为什么在内存中存储的是补码呢?

使用补码、可以将符号位和数值域统一处理,同时,加法和减法也可以统一处理。在CPU中只有加法器,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。

我们举个例子:

1-1

因为CPU中是只有加法器的,那么实现减法就是1+(-1)即可。

 1的补码:00000000 00000000 00000000 00000001

-1的补码:111111111 11111111 11111111 11111111

1+(-1):00000000 00000000 00000000 00000000

同理在执行乘法与减法中,就是多个数相加,多个数相减。

经过上面的例子,我们确实可以得到,存储的就是补码,但是为什么存储的顺序有些变动呢?

大小端的介绍

我们在创建变量时,操作系统就会给你分配空间,比如你创建了【short/int/double/float】的变量,这些变量的类型都是大于1个字节的,操作系统会根据你这个变量的类型,分配相应的内存空间,在空间分配好之后,在这块内存中进行存储这个变量,反正内存已经分好了,所以在这个内存中如何存储就不关操作系统的事情了。那么这个变量在它内存存储中,是从低地址到高地址呢?还是从高地址到低地址呢?

在一块给定的内存中,其实在内存中存储的时候,其实顺序是可以任意的。只要以你的方式存储进去,关键的是在你需要使用数据的时候,以你的方式原样返回即可。

简而言之,怎么存储其实并不重要,重要的是,你能够存进去并且取出来即可。

如下图,假设int a = 0x11223344,其实下面的这类种存储方式都是可以的,但是因为像类似下面这两个存储方式,在读取的时候,是极其不方便的,所以这之后的C语言发展的过程中,就将类似于下面的存储方式就不再继续使用了,最后就采用了这两种存储方式,一种是正着存储,一种是倒着存储。

这里这就分为了两种存储方式,大端字节序存储和小端字节序存储。 

大端字节序存储

大端字节序存储,就是将一个数据的低位字节处的数据存储到内存中高位字节的存储数据,将这个数据的高位字节处的数据存储到内存中低位字节的存储数据。

小端字节序存储

小端字节序存储,就是将一个数据的高位字节处的数据存储到内存中高位字节的存储数据,将这个数据的低位字节处的数据存储到内存中低位字节的存储数据。

而这里注意,在数据类型为char时,就不需要考虑存储顺序的问题,因为char类型的数据只占1个字节。

只有所占的字节数大于1字节的时候,比如short、int、double、float、long long等,我们才考虑存储顺序的问题。

那么为什么会出现大小端字节序的存储问题呢?

因为在计算机系统中,我们是以字节为单位的,每个地址单元都对应着一个字节,一字节为8比特位。但是在C语言中,除了有8比特位的char,还有16比特位的short,32比特位的long(具体需要根据编译器)。另外,对于16位、32位的处理器,由于寄存器是大于一个字节的,那么必然就会存在着如何将一个多字节存储安排的问题,这便导致了会有大小端字节序存储之分。

在学习完了大小端字节序存储之后,这里有一道关于它的笔试题:

百度在2015年系统工程师的笔试题:

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

思路:

假如我们存储了int a=0,那么a的16进制就是0x 00 00 00 01。在这个数据中,01方向的是低位数数据,00方向的是高位数数据。那么,在这种情况下存储1,假设是小端字节序存储(高位数数据存储到高地址中),那么就是01存储到了高地址中,那么只要验证出低地址中取出的数等于1的话,那么就可以说明它就是小端字节序存储。如图:

代码:

#include <stdio.h>
int main()
{int a = 1;char* p = (char*)&a;//利用char类型1字节,来判断存储在低地址种的一字节的数据是何值if (*p == 1){printf("小端");}else{printf("大端");}return 0;
}

下面有几个例题,我们来看一下:

整型存储相关例题

  • 下面代码的运行结果是什么:
#include <stdio.h>
int main()
{char a = -1;signed char b = -1;unsigned char c = -1;printf("%d %d %d", a, b, c);return 0;
}

解析:

  • -1的补码:11111111 11111111 11111111 11111111
  • 上面的是-1在整型的存储方式,而char只占1个字节,所以char -1在计算机内存中存储的补码就是:11111111
  • %d是以十进制的形式打印有符号的整型
  • 但是现在是char类型,并不是整型类型,所以整型提升。
  • 整型提升的规则:对于有符号的数来说,整形提升补充的是它的符号位;而对于无符号整型来说补充的是0。
  • 又因为char类型在大多数的编译器上是有符号的,所以整型提升的是符号位。
  • 所以在打印出来的char a=-1的补码就是:11111111 11111111 11111111 11111111
  • 所以对应的整型就是-1,所以第一个值打印出来的结果是-1。

运行结果:

-1 -1 255

  •  下面代码的运行结果是什么:
#include <stdio.h>
int main()
{char a = -128;printf("%u\n", a);
}

 解析:

  • -128的补码:11111111 11111111 11111111 10000000
  • 但是上面的是整型的-128,整型占4个字节,而char类型只占1个字节,所以char -128在计算机内存中只能存储的补码为:10000000
  • %u是十进制的形式,打印无符号的整数;
  • 而现在这个-128不是整型类型,而是字符类型的,所以就需要整型提升;
  • 整型提升的规则:对于有符号的数来说,整形提升补充的是它的符号位;而对于无符号整型来说补充的是0。
  • 又因为char类型在大多数的编译器上是有符号的,所以整型提升的是符号位;
  • 所以在打印出来的char a=-128的补码就是:11111111 11111111 11111111 10000000
  • 而%u打印的是无符号的整数,所以整个二进制中的所有位都是有效位,没有符号位;
  • 所以对应的整型就是4,294,967,168,所以第一个值打印出来的结果是4,294,967,168。
  •  下面代码的运行结果是什么:
#include <stdio.h>
int main()
{char a = 128;printf("%u\n", a);
}

 解析:

  • 128的补码:00000000 00000000 00000000 10000000
  • 但是上面的是整型的128,整型占4个字节,而char类型只占1个字节,所以char128在计算机内存中只能存储的补码为:10000000
  • %u是十进制的形式,打印无符号的整数;
  • 而现在这个128不是整型类型,而是字符类型的,所以就需要整型提升;
  • 整型提升的规则:对于有符号的数来说,整形提升补充的是它的符号位;而对于无符号整型来说补充的是0。
  • 又因为char类型在大多数的编译器上是有符号的,所以整型提升的是符号位;
  • 所以在打印出来的char a=128的补码就是:11111111 11111111 11111111 10000000
  • 而%u打印的是无符号的整数,所以整个二进制中的所有位都是有效位,没有符号位;
  • 所以对应的整型就是4,294,967,168,所以第一个值打印出来的结果是4,294,967,168。
  •  下面代码的运行结果是什么:
#include <stdio.h>
int main()
{int i = -20;unsigned int j = 10;printf("%d\n", i + j);
}

 解析:

  • -20的补码:11111111 11111111 11111111 11101100
  • 10的补码:00000000 00000000 00000000 00001010
  • -20的补码+10的补码的结果补码:11111111 11111111 11111111 11110110
  • 原码:10000000 00000000 00000000 00001010

运行结果:-10

  •  下面代码的运行结果是什么:
#include <stdio.h>
int main()
{unsigned int i;for (i = 9; i >= 0; i--){printf("%u\n", i);Sleep(1000);}return 0;
}

 

解析:

注意这里是无符号的char类型,任何一个无符号整数都是大于等于零的。无符号的那么它的每一位都是有效数字,在减到0之后,变为:

11111111 11111111 11111111 11111111

11111111 11111111 11111111 11111110

11111111 11111111 11111111 11111101

11111111 11111111 11111111 11111100

......

i这里一直被看做是整数并且i大于等于0,所以就进入了死循环中。

unsigned与signed的轮回:

 

  •  下面代码的运行结果是什么:
#include <stdio.h>
int main()
{char a[1000];int i = 0;for (i = 0; i < 1000; i++){a[i] = -1 - i;}printf("%d", strlen(a));return 0;
}

解析:

  • strlen统计的是“\0”之前出现的字符的个数
  • “\0”是一种以“\ddd”的形式的字符,它的ASCII码值为0
  • 所以此题也就是统计这个数组里面什么时候会遇到0
  • char在大多数编译器是有符号的,所以:
  • 运行过程为:-1 -2 -3 -4 -5 -6...-128 127 ...6 5 4 3 2 1
  • 128+127=255

运行结果:255

  •  下面代码的运行结果是什么:
#include <stdio.h>
unsigned char i = 0;
int main()
{for (i = 0; i <= 255; i++){printf("hello world\n");}return 0;
}

解析:

无符号的char的取值范围为0~255

所以i<=255,所以死循环


http://www.ppmy.cn/news/1148542.html

相关文章

Mysql-数据库和数据表的基本操作

Mysql数据库和数据表的基本操作 一.数据库 1.创建数据库 创建数据库就是在数据库系统中划分一块空间存储数据 &#xff08;1&#xff09;语法 create database 数据库名称;&#xff08;2&#xff09;查看数据库 show create database 数据库名;&#xff08;3&#xff09;…

思维导图解读《思考,快与慢》

这本《思考&#xff0c;快与慢》&#xff0c;是诺贝尔经济学奖获得者卡尼曼的代表作。特沃斯基与卡尼曼和塞勒共同开创了行为经济学&#xff0c;不同之处在于: 卡尼曼和特沃斯基是从心理学攻入经济学阵营&#xff0c;而塞勒则是打开了经济学的大门&#xff0c;欢迎心理学的进入…

了解5个区别,FPmarkets用烛台和Renko图实现交易翻倍

很多投资者不知道日本烛台图表和Renko图表的区别&#xff0c;在交易中出现好的机会而把握不住&#xff0c;今天FPmarkets就和投资者一起了解烛台图表和Renko图表的区别&#xff0c;在今后的交易中能第一时间抓住机会&#xff0c;从而盈利。 首先&#xff0c;Renko图表是平滑的…

RecyclerView 空白区域点击事件

在项目中使用RecyclerView展示列表数据&#xff0c;用了GridLayoutManager&#xff0c;在遇到item个数不满足一行时&#xff0c;会在页面右侧透出空白位&#xff0c; 如下图所示. 目前点击空白位是没有点击响应事件的&#xff0c;我们想实现点击响应以扩大用户可以进入LandingP…

蓝桥等考Python组别十六级008

第一部分:选择题 1、Python L16 (15分) a和b是两个集合,a&b表示a和b的( )。 并集交集子集差集正确答案:B 2、Python L16 (15分) 运行下面程序,输出的结果是( )。

大数据之Hudi数据湖_基本概念_时间轴_TimeLine---大数据之Hudi数据湖工作笔记0005

然后看一下hudi的,时间轴概念,很简单了,就是之前说的时间旅行,其实就是 比如在某个时间点,记录,这个时间点做了什么,就是这个意思 然后像回去看看的时候,可以找到这个时间点做了什么 一个时间点就是一个Instant (时刻 瞬间的意思) 可以看到时刻的解释 instant 时刻instant包…

蓝桥等考Python组别十七级004

第一部分:选择题 1、Python L17 (15分) 运行下面程序,输出的结果是( )。 def func(x, y): return (x - y) // 2 print(func(10, 4)) 2356正确答案:B 2、Python L17 (15

类图 UML从入门到放弃系列之二

1.劝退说明(开个玩笑) UML包含有许多小组件、修饰符以及其他小巧复杂的东西。UML的内容相当庞大&#xff0c;以至于你可以花大量的时间把自己修成一个UML语言律师&#xff0c;并能够完成所有律师能够完成的工作&#xff1a;编写出所有人都无法理解的文档。现在流行的敏捷开发倡…