深度剖析数据在内存中的存储(上)

1.数据类型介绍:

char        //字符数据类型
short       //短整型
int         //整形
long        //长整型
long long   //更长的整形
float       //单精度浮点数
double      //双精度浮点数
1.1类型的基本归类:

char char虽然是字符类型,但是字符类型存储的时候,存储的是字符的ASCII码值,ASCII值是整数

unsigned char

signed char

short

unsigned short [int]

signed short [int]

int

unsigned int

signed int

long

unsigned long [int]

signed long [int]

为什么要区分有符号或者无符号?因为生活中我们在描述的时候例如一个人的身高,年龄肯定就是无符号的了,温度就有正有负为无符号的。

short—2byte-16bit

0000 0000 0000 0000

0000 0000 0000 0001

0000 0000 0000 0010

..................................

1111 1111 1111 1111

如果是有符号的数据,最高位是符号位,最高位是0,表示正数,最高位是1,表示负数

如果对于无符号数来说,最高位也是数据位不是符号位

int short,在创建的时候如果没标明是unsigned 类型就默认signed类型,但是char类型没说我们就不确定有符号无符号,这个取决于编译器。

浮点数家族:

float

double

构造类型:(也可以叫自定义类型)

> 数组类型(int arr[10];类型是:int [10])

> 结构体类型 struct

> 枚举类型 enum

> 联合类型 union

指针类型

int *pi;

char *pc;

float* pf;

void* pv;

空类型:

void 表示空类型(无类型)

通常应用于函数的返回类型、函数的参数、指针类型

2. 整形在内存中的存储

我们之前讲过一个变量的创建是要在内存中开辟空间的。空间的大小是根据不同的类型而决定的。

int a = 20;

int b = -10

2.1 原码、反码、补码

计算机中的整数有三种表示方法,即原码、反码和补码。 三种表示方法均有符号位和数值位两部分,符号位都是用0表示“正”,用1表示“负”,而数值位 负整数的三种表示方法各不相同

整数可以写出三种2进制形式

原码

直接将二进制按照正负数的形式翻译成二进制就可以

反码

将原码的符号位不变,其他位依次按位取反就可以得到了。

补码

反码+1就得到补码。

正数的原、反、补码都相同。

对于整形来说:数据存放内存中其实存放的是补码

 

通过内存监控也可以看出是ff ff ff f6 

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

cpu中只有加法器,那怎么算减法呢?
1-1<=>1+(-1)

乘法跟除法无非也是多加几次或者多减几次

1:0000 0000 0000 0000 0000 0000 0000 0001

-1:1000 0000 0000 0000 0000 0000 0000 0001

sum:1000 0000 0000 0000 0000 0000 0000 0010

这里得到到结果是-2,但是1-1的结果应该是0,这里明显的计算错误了

我们看看补码的计算效果:
1:正整数原反补都一样

0000 0000 0000 0000 0000 0000 0000  0001

-1:

1111 1111 1111 1111 1111 1111 1111 1111

此时计算1-1

10000 0000 0000 0000 0000 0000 0000 0000

这时1是超出范围的,得出结果的时候这个值就丢了于是得到了

0000 0000 0000 0000 0000 0000 0000 0000 

此时我们发现结果正确了

拓展:

signed char一个字符是1个byte->8个bit

0000 0000 解析:  0

0000 0001              1

0000 0010              2

.................

0111 1111                127 

..................

1000 0000       解析的时候由于这个负数不能直接减一,所以直接解析位-128

1000 0001  先-1,再取反:1000 0000-1111 1111 :-127

.................

1111 1111    解析:1111  1110-1000 0001:-1

所以一个有符号的char存的值的范围只可能是:-128~~127

如果是unsigned char范围就是:0~255

如果unsigned和signed混用会有什么后果

%u:打印无符号数,意思是要打印的一定是无符号数,不是无符号数,我也认为是无符号数

-10:

原码:1000 0000 0000 0000 0000 0000 0000 1010

反码:1111 1111 1111 1111 1111 1111 1111 0101

补码:1111 1111 1111 1111 1111 1111 1111 0110

如果是unsigned int我们就认为他没符号位,

所以我们认为他的原码就是1111 1111 1111 1111  1111 1111 1111 0110使用计算器

答案正好是这个结果

%d:是打印有符号数,意思是要打印的一定是有符号数,不是有符号数,我也认为是有符号数

现在我们在谈一下数据储存为什么是倒着存储?

这里就涉及一个问题叫做大小端, 

2.2 大小端介绍;(全称叫做大小端字节序存储)

什么大端小端:

比如说一个整型 16进制:0x 11 22 33 44(4个字节)一个16进制位可以转化为四个二进制位,两个16进制位可以转化为8个二进制位。

乱序的储存逻辑太复杂了,我们经过思考只留下了这两种存储排序。

第一种存储方式我们称为:大端字节存储:

第二种存储方式我们称为:小端字节存储:

是按照字节为单元来存储。

大端(存储)模式,是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址 中;

小端(存储)模式,是指数据的低位保存在内存的低地址中,而数据的高位,,保存在内存的高地 址中。

为什么有大端和小端:

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

例题:请简述大端字节序和小端字节序的概念,设计一个小程序来判断当前机器的字节序

int main()
{
    int a = 1;
    //补码;0000 0000 0000 0000 0000 0000 0000 0000 0001
    //16进制: 0   0   0     0    0    0    0   0     1
    //=>0x 00 00 00 01
    char*p=(&a);
    if (*p == 1)
    {
        printf("小端\n");
    }
    else
    {
        printf("大端\n");
    }
    return 0;
}

 //输出什么?
int main()
{
    char a = -1;
    //-1的原码:1000 0000 0000 0000 0000 0000 0000 0001
    //-1的反码:1111 1111 1111 1111 1111 1111 1111 1110
    //-1的补码:1111 1111 1111 1111 1111 1111 1111 1111
    //这里是char(1个字节=8个bit位)
    //1111 1111 打印不够一个整型怎么办?
    signed char b = -1;
    unsigned char c = -1;
    printf("a=%d,b=%d,c=%d", a, b, c);
    //当我们打印a的时候,发生整型提升
    //整型提升补的原来的符号位
    //补完之后:1111 1111 1111 1111 1111 1111 1111 1111
    //打印是有符号数,这是补码,打印是打印原码,得到原码
    //1000 0000 0000 0000 0000 0000 0000 0001(-1)
    //char b跟a思路相同
    //c:
    //-1的原码:1000 0000 0000 0000 0000 0000 0000 0001
    //-1的反码:1111 1111 1111 1111 1111 1111 1111 1110
    //-1的补码:1111 1111 1111 1111 1111 1111 1111 1111
    //char c:1111 1111,整型提升,补满之后
    //0000 0000 0000 0000 0000 0000 1111 1111(对于无符号数直接补0)
    //这里按照%d打印,这个是正数,补码就是原码
    //输出结果为255
    return 0;
}

 

 

 

 

 

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

-Taco-

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

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

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

打赏作者

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

抵扣说明:

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

余额充值