C/C++数据在内存中的存储方式

C语言给定了一些基本的数据类型

char   类型

int    类型   

long   类型


float  类型

double 类型

前三者都属于整型家族,后两者属于浮点型家族,为什么char属于整型家族呢?这是因为字符是以对应的ASCII码值存放到内存中,本质上是属于整数的,所以将其归类为整型。

那么我们在创建这些变量且赋予它们具体数值的时候,它们又是如何将这些数值数据存放到内存中的呢?我们都知道任何形式的数据(字符,图片, 音频......)放到电脑中都是以二进制的形式存放的

例如 我们创建一个整型变量,并赋予它一个数值

int  a = 10;

a中的数据放在内存中需要转化为二进制形式

将10转化为二进制形式为 1010 因为int有32个比特位的大小,所以最终的形式如下

00000000000000000000000000001010

这样我们就解决了存放整数的问题,但很快会发现,如果存放一个负整数该怎么存呢?该怎么表示这个数字是负的呢?聪明的科学家想到了这样一个办法,那就是将这个二进制序列的最高位定义为符号位,如果最高位上的数字为0,那表示这个数字是正数,如果最高位上的数字为1,那表示这个数字是负数,例如下面的表示

int  a = 10;

a中的数据放在内存中转化为二进制形式为

00000000000000000000000000001010



int a = -10;

a中的数据放在内存中转化为二进制形式为

10000000000000000000000000001010

好了,现在正整数和负整数在内存中存放的形式都解决了,哈哈,不过问题又来了,正整数和负整数在进行加减运算的时候就出现问题了,如下

int a = 10;
int b = -1;

     a 在内存中的二进制形式为  00000000000000000000000000001010
假设 b 在内存中的二进制形式为  10000000000000000000000000000001

如果将a 与 b 相加 

a 00000000000000000000000000001010
+
b 10000000000000000000000000000001

= 10000000000000000000000000001011

a和b相加,结果却变成了 -11,怎们办,这里不得不佩服科学家的聪明,引入了原码 补码 反码的概念,那么这些具体有什么作用呢?接下来分析一下。

正数的原码 反码 补码相同

int a = 10;

a 的二进制形式为 
00000000000000000000000000001010  //原码
00000000000000000000000000001010  //反码
00000000000000000000000000001010  //补码



负数的反码为 原码的符号位不变,其他位取反。负数的补码为反码加1

int b = -10;
 
b 的二进制形式为
10000000000000000000000000001010  //原码
11111111111111111111111111110101  //反码
11111111111111111111111111110110  //补码

这里需要知道的是,无论正数还是负数,都是以补码的二进制序列放到内存中的
因此实际上,a放到内存中是补码  00000000000000000000000000001010

b放到内存中是补码  11111111111111111111111111110110,接下来我们通过编译器去验证一下。

 验证结果是没有问题滴,说明确实是这样存储的,但至此,可能对其作用仍不了解,那接下来说一下反码 补码的具体的作用,还是以加法为例

int a = 10;

a 的二进制形式为 
00000000000000000000000000001010  //原码
00000000000000000000000000001010  //反码
00000000000000000000000000001010  //补码

int b = -5;
 
b 的二进制形式为
10000000000000000000000000000101  //原码
11111111111111111111111111111010  //反码
11111111111111111111111111111011  //补码

因为都是以补码存放到内存中,所以在内存中
a 为00000000000000000000000000001010
b 为11111111111111111111111111111011

a 00000000000000000000000000001010
+
b 11111111111111111111111111111011

= 00000000000000000000000000000101

转化为十进制就是5  是不是很奇妙,这就是反码 补码的妙用,非常漂亮的的解决掉正负数进行加减运算的问题,其他的乘除运算都可以转化为加法运算,这样也就同时解决掉其他的运算问题。

这里补充一点,补码取反加1是可以转化为原码的

我们试一试

11111111111111111111111111111011  这是b的补码,现在我们符号位不动,其他位取反

10000000000000000000000000000100  这是补码取反后的结果,接下来我们把它加1

10000000000000000000000000000101  这是加1后的结果,发生什么事了,是不是又变回了原码

接下来,举点栗子,加深对上面知识的理解

char a = -1;                   //注:char 是 signed char还是 unsigned char取决于编译器
                                          
signed char b = -1;                                         //不过一般都是signed char

unsigned char c = -1;

printf("%d %d %u", a, b, c);

那么打印的结果分别是什么呢?

一起分析一下吧

以上就是整型数字在内存中的存储方式,但仔细观察下图,会发现一个奇怪的现象

就拿int a 来说 我们分析的存储方式应该是00 00 00 0a  ,而编译器上却显示存储形式为0a 00 00 00

这其实就牵涉到大端,小端存储的问题,为什么会这样呢?因为int 类型接连占用四个字节,该空间的数据的存放位置就得有一个规定,我们同样可以将a中得数据以00 0a 00 00或者是00 00 0a 00的方式存储,但这些都太过麻烦了,00 00 00 0a 和 0a 00 00 00这两种存储方式最符合我们的思维习惯,就沿用至今,分别叫大端存储法和小端存储法。大端还是小端是取决于硬件的,与编译器无关,一般个人pc用的是小端存储法。

接下来我们一起分析一下大端与小端,然后再写个小程序来判断自己的设备采用的是大端还是小端

 那我们怎么判断自己的设备是大端还是小端呢? 同样是 int a = 10; 我们想看看它是大端存放还是小端存放,只需查看该空间的第一个字节,因为空间的第一个字节是处于低地址的,如果该空间的第一个字节没有存放 0a 则说明是大端存放,如果存放了0a,则说明是小端存放。

代码实现如下

int main()
{
  int a = 10;
  
  char *p = (char*)&a;
  
  if (10 == *p)
  { 
       printf("小端\n");
  }
  else
  {
       printf("大端\n");
  }
  return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

浪雨123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值