2. 整型在内存中的存储
变量的创建时要在内存中开辟空间的,空间的大小是根据不同的类型而决定的
而开辟空间后,数据在所开辟内存中是如何存储的呢?
(1). 整数用二进制表示的三种表示形式:原码、反码、补码
原码:
正数:直接将数值按照正负数的形式翻译成二进制得到原码
负数:直接将数值按照正负数的形式翻译成二进制得到原码
或者
反码按位取反得到原码
再或者
补码按位取反,再+1得到原码
反码:
正数:原码、反码、补码 都相同
负数:原码的符号位不变,将其它位依次按位取反得到反码
或者
补码-1得到反码
补码:
正数:原码、反码、补码 都相同
负数:反码+1得到补码
(2). 符号位 和 数值位(整数)
上面三种表示形式都有 符号位 和 数值位 两部分
符号位:
二进制最高位的一位叫做符号位,
符号位 用 0 表示 “正”;
符号位 用 1 表示 “负”。
数值位:
除了符号位,其它位都是数值位
对于正数:原码、反码、补码 都相同
对于负数:三种表示方法各不相同(参考上面)
(演示代码:)
#include <stdio.h> int main() { int num = 10;//创建一个叫num的整型变量,这时num向内存申请4个字节来存放数据 // 4个字节 - 32比特位 //00000000000000000000000000001010 -- 原码 //00000000000000000000000000001010 -- 反码 //00000000000000000000000000001010 -- 补码 int num2 = -10; //10000000000000000000000000001010 -- 原码 //11111111111111111111111111110101 -- 反码 //11111111111111111111111111110110 -- 补码 return 0; }
对于整型来说:数据存放在内存中其实存放的是补码
在计算机系统中,数值一律用 补码 来表示和存储。
原因在于:使用补码,可以将符号位和数值位统一处理(把符号位也看成数值位来计算)
同时,加法和减法也可以统一处理(CPU只有加法器),
此为,补码和原码相互转换,其运算过程是相同的
(原码转换为补码按位取反再+1,补码转换为原码也可以按位取反再+1),
只有加法器,计算减法时:1 - 1 --> 1 + (-1) ,
假设计算用的是原码,算出来的是 -2,是错误的
而用补码进行计算后,再用原码表示,结果则是对的
(为什么会倒着存储呢?)
(3). 大小端介绍
字节序:
以字节为单位,讨论存储顺序(大端字节序存储 / 小端字节序存储)
低位 / 高位:
十进制:数字123,1是百位,2是十位,3是个位。这里的1就是高位,3就是低位。
十六进制:0x 11 22 33 44,这里 11 就是高位,44就是低位。
大端字节序存储:
大端(存储)模式:指数据的低位字节内容保存在内存的高地址中,而数据的高位字节内容,保存在内存的低地址中;
(低位高地址,高位低地址)
小端字节序存储:
小段(存储)模式:指数据的低位字节内容保存在内存的低地址中,而数据的高位字节内容,保存在内存的高地址中;
(低位低地址,高位高地址)
为什么有大端和小段:
一个数据只要超过一个字节,在内存中存储的时候就必然涉及到顺序的问题,所以要有大端和小端的存储模式对该数据进行排序。
为什么会有大小端模式之分,是因为在计算机系统中,我们是以字节为单位的,每个地址单元都对应着一个字节,一个字节为 8bit 。但是在C语言中除了 8bit 的 char 之外,还有 16bit 的 short 类型,32位的 long 类型(具体要看编译器),另外,对于位数大于8位的处理器,例如 16位 或者 32位 的处理器,由于寄存器宽度大于一个字节,那么就存在着一个如何将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式 。
例如:一个 16bit 的 short 类型 x ,在内存中的地址为 0x0010,x 的值为 0x1122,那么 0x11 为高字节,0x22 为低字节。对于大端模式,就将 0x11 放在低地址中,即地址 0x0010 中, 0x22 放在高地址中,即地址 0x0011 中。小端模式则相反。
我们常用的 x86 结构是小端模式,所以上面的图数据会“倒着放”,低位字节放在了低地址,高位字节放在了高地址。而 KEIL C51 则为大端模式。很多的ARM,DSP都为小端模式。有些ARM处理器还可以由硬件来选择为大端模式还是小端模式。
(4). 写个程序判断大小端:
思路:
有变量a,存放在内存中的十六进制数为:01 00 00 00(小端存储),地址第一位是1,
如果是大端存储:则应该是:00 00 00 01,地址第一位是0,
可以把 a 的地址取出第一位,如果
第一位地址 == 1,说明是小端存储,
第一位地址 == 0,说明是大端存储,
取出 int类型a 的 地址 第一位方法:
*(char*)&a
把 int* 强制转换为 char*,再解引用,即可取出一位地址的内容。
实现代码:
#include <stdio.h> int check_sys() { int a = 1; //要大于一个字节的数据,才有顺序可言 判断大小端 //if (*(char*)&a == 1) // //把int*强制转换a的地址为char*再解引用,判断地址第一位的内容 //{ // return 1; //} //else //{ // return 0; //} //可以直接写成 return *(char*)&a; } int main() { int ret = check_sys(); if (ret == 1) { printf("小端\n"); } else { printf("大端\n"); } return 0; }