上一篇文章中,我们了解了关于c语言中类型的分类,在这部分前置知识的基础上,这篇文章,我们会一起学习整型数据是怎么样在内存中存储的。
变量的创建要在内存中开辟空间来存储,那么,数据在所开辟的内存空间中到底是如何存储的呢?让我们一步一步来搞清楚这个问题。
首先来看看整型以什么形式存储,即整型的原码反码和补码,再学习整型存储的不同顺序,也就是不同的存储模式。
原码、反码、补码
人们将计算机中用来表示带符号二进制数的数称为机器数。整数的机器数有3种表示方法:原码、补码和反码。三种表示方法均由两部分构成,分别是第一位的符号位和剩下的数值位。所有机器数符号位都用0表示“正”,1表示“负”,而正数和负数的数值位表示方法各有不同。
原码
直接将二进制按照正负数的形式翻译成二进制,就得到了数据的原码。最高位表示符号, 正数为0, 负数为1。
反码
原码的符号位不变,其他位依次按位取反,就得到反码。
补码
反码+1就得到补码。所有整型在内存中存储的,其实都是补码。
例子:
int a=9;
原码:00000000000000000000000000001001
反码:00000000000000000000000000001001
补码:00000000000000000000000000001001
int b=-1;
原码:10000000000000000000000000000001
反码:11111111111111111111111111111110
补码:11111111111111111111111111111111
注意:正数的原反补码都相同,负数的原反补码各不相同。
为什么有原反补码之分?为什么整型在内存中存储的是补码?
因为在CPU中只有加法器而没有减法器,若没有原反补码而只有原码,在计算减法时,需要转化为负数加法,很容易出错。
例如:
int a=9;
原码:00000000000000000000000000001001
int b=-1;
原码:10000000000000000000000000000001
00000000000000000000000000001001
10000000000000000000000000000001
9-1的结果:
10000000000000000000000000001010
转化为10进制:-10
为了解决原码减法的问题,于是,出现了反码。反码虽然解决了加减法的问题,但是仍有不足。例如:
int a=1;
反码:00000000000000000000000000000001
int b=-1;
反码:11111111111111111111111111111110
a+b:
反码:11111111111111111111111111111111
原码:10000000000000000000000000000000 结果为-0
为了解决0与-0的问题,我们又引入了补码,引入补码之后,问题都得到了较好的解决。
int a=1;
补码:00000000000000000000000000000001
int b=-1;
补码:11111111111111111111111111111111
a+b:
补码:00000000000000000000000000000000
反码:00000000000000000000000000000000
原码:00000000000000000000000000000000 结果为0
所以,在计算机系统中,数据分为原码反码和补码三种,并且,不论整数或负数,一律用补码来表示和存储。
大/小端存储模式
什么是大/小端?
我们通常说的大/小端,其实指的是大端字节序存储模式和小端字节序存储模式,它们是在内存中数据存储的两种不同方式。
大端模式:数据的低位保存在内存的高地址中,而数据的高位保存在内存的低地址中;
小端模式:数据的低位保存在内存的低地址中,而数据的高位保存在内存的高地址中。
为什么有大/小端之分?
这是因为在计算机系统中一般以字节为单位,每个内存单元都对应一个字节(8bit)。但是在C语言中除了char(大小1个字节)之外,还有short(大小2个字节),int(大小4个字节)等,对于位数大于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如何将多个字节安排的问题。因此,就出现了大端存储模式和小端存储模式。
例如:
一个 16bit 的 short 型 x ,在内存中的地址为 0x0010 , x 的值为 0x1122 ,那么 0x11 为高字节, 0x22 为低字节。
对于大端模式,将 0x11 放在低地址,即 0x0010 中, 0x22 放在高地址,即 0x0011 中。
小端模式则刚好相反,将 0x22 放在低地址,即 0x0010 中, 0x11 放在高地址,即 0x0011 中。
一个程序验证大/小端
虽然接触了大端和小端的概念,但我相信大家仍有一些迷糊,接下来,就让我们用一个具体的例子来具体说明。
#include <stdio.h>
int main() {
int a = 1;
char * p= (char*)&a;
//00000000 00000000 00000000 00000001
// 0x0001
//若小端存储 01 00 00 00
//若大端存储 00 00 00 01
if(p) printf("小端");
else printf("大端");
return 0;
}
结果(vs2022):
![](https://img-blog.csdnimg.cn/img_convert/5e3bf19e96894bf49453ba52662ab106.png)