数据类型在内存如何存储

俗话说:不同的盒子用来装不同的东西,C语言将基本类型分为了整形、浮点型、字符型、指针。还有像数组、结构体、枚举类型、联合体等一些自定义类型。合理使用类型可以达到节省空间的目的,那么就将这些类型简单的进行一个分类。

整形类型

char之所以是放在整形类型,是因为字符型在c语言中是以ASCII码表的形式存储的,每个字符都对应了一个整形数字。

signed是有符号的,unsigned代表了无符号 ,无符号就是数字没有符号位,所以也就不存在负数,而只有正数,无符号数需要用%u来打印。其中除了signed char以外,其他类型在加signed都等价于不加signed。

signed char和unsigned char

char是signed char还是unsigned char这就取决据编译器,不同的编译器结果可能会不一样。常见的编译器char是signed char,但也有小部分的编译器char是unsigned char。

浮点型

浮点型就是平常说的小数,浮点型有两种,一种叫单精度浮点型,另一种叫双精度浮点型。所谓双精度浮点型就是比单精度浮点型的精度要更高一些。浮点型是存在精度的,所以有些时候会发生精度丢失的问题。

构造类型

构造类型又叫做自定义类型,就是由程序员自己来创造的类型。

 数组是一种类型,数组名去掉剩下的就是数组的类型,比如:int [10],就是一个数组类型,它的类是int 它的类型大小是10个int即(40个字节)。如果将数组的空间大小稍作修改,比如改成了5,那么int [10] 和int [5]肯定就不一样。如果只要能通过改动它的类型就发生变化的话,那它就属于自定义类型。

指针类型

指针也是一种类型,指针类型是一种专门用来存放不同类型地址的类型,也是C语言非常常用的一种类型。

 空类型

void表示空类型(无类型),通常用来做函数参数、函数返回类型、指针类型

void* 就是空类型的指针类型

布尔类型

C语言在早期是没有布尔类型这个概念的,布尔类型是在C99标准之后才从C++中引入的,布尔类型是用来表示真假的,布尔类型是用true表示真,用false表示假。但C语言本身就已经用0表示假非0表示真了。所以说C语言引入布尔类型其实并没有什么特别大的意义。如果想使用布尔类型需要引下头文件#include<stdbool.h>,_Bool。

#include<stdio.h>
#include<stdbool.h>
int main()
{
    _Bool flag = false;//true 真
    if(flag)
        printf("真\n");
    else
        printf("假\n");

    return 0;
}

 整形在内存中的存储

整形分为正数或负数。计算机中存储的是二进制数,那么计算机是如何存储负数的?要了解计算机如何存储负数,首先就了解下原码反码和补码。

原码、反码和补码

计算机使用的是二进制,计算机用强电来表示1,用弱电来表示0。生活中使用的数字是十进制的。当把十进制数转换为二进制就是这个数的原码。二进制的每一位都代表2^n。

原码

将数字从十进制转为二进制就得到了它的原码,而第一个位置叫做符号位,用来确定该数是正数还是负数。如果这个数是正数那么它的符号位是0,如果是一个负数那么它的符号位就是1。正数不存在补码,它的原码就是补码。

假设一个int类型的整形数字,一个int整形在内存中是4个字节,一个字节是8个比特位。8*4=32,所以当在画int类型的二进制时,最好是将32个比特都能画出来。

 补码

在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统 一处理;
同时,加法和减法也可以统一处理( CPU 只有加法器 )此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。

所以计算机真实存储的其实是补码,当计算机需要存储一个整数时会先将它从原码转化为补码。当需要从内存中取出时才会再将它转变为原码。由于因为正数没有补码的概念,所以正数的原码就是补码。只有当负数在进行存储时才会去将它从原码转换成补码。

那么,如何转换?首先计算机会先将该负数的原码,除了符号位以外,其他位按位取反得到反码,再从反码的基础上,进行+1的操作,才能得到补码然后存储到计算机里。

如果想要将补码转换成补码也只需要再取反+1就行。

数值取值范围

当知道数据如何存储之后,就不得不聊聊数值的取值范围了。一个有符号数和无符号数,它们的取值范围会是多大。

#include<stdio.h>
int main()
{
    char a = 128;
    printf("%d\n", a+1);//-127

    unsigned char b = 255;
    //unsigned char  0~255
    printf("%d\n", b + 1);//0

    return 0;
}

知道一个有符号int是32个比特位,其中一位是符号位,那么一个有符号的int的正数取值范围就是2^31-1,负数的取值范围则是2^31。无符号数因为不存在负数,那么它就没有符号位,所以一个无符号的int的取值范围是0~2^32-1。

假设一个32个全1的有符号数的二进制位+1得到的肯定是要比32位全1还要再多一个1,但是一个int类型只能存储32个字节的数据,所一就会发生截断。截断之后就是全0,然后这个数会重新从0开始。

浮点型在内存中的存储

浮点型有两种表示方式,一种是平常的3.1415926,另一种是以科学计数法的形式表示1E10(1.0*10^10)。

浮点型是由国际标准(电气和电子工程协会):IEEE 754,规定任意一个二进制浮点数num都可以表示为(-1)^S * M * 2^E其中(-1)^S表示的是符号位,当S=0时,则该数是一个正数,如果为1,则该数字是一个负数。M表示有效数字,大于等于1,小于2。2^E表示指数位。

假设一个5.0,它的二进制是101.0,写成(-1)^S*M*2^E的形式就是1.0×2^2,S = 0,M = 1.01,E = 2;如果是-5.0,它的二进制就是 -101.0,也就等价于-1.0×2^2,S = 1,M = 1.01,E = 2。

简单来说,就是S表示正负数,M就是将小数点前的二进制位向右移动,小数点前只留一个1,E则是小数点前右移N位,然后加上中间值。

IEEE 754规定,32位的浮点数,最高位一位S用来表示符号位,向后8位指数位E,其余23位有效数字M。

如果是64位浮点数,最高一位S表示符号位,后11位指数位E,其余52位为有效数字。

IEEE 754对M和E还有一些特别的规定:

前面说,M是有效数字,2>M>=1,所以写成1.xxxxx的形式,其中xxxxx是有效数字,那么M前面总数1,IEEE 754就规定,在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的xxxxx部分,在读取的时候也只需要在把前面的1补上去。这样做是为了能够节省一位有效数字。如果是32个比特位留诶M的只有23位,将第一位去掉之后,就可以保存24位。

E是一个无符号整数。如果E为8位,那么它的取值范围就是0~255,如果是11位,则取值范围在0~2047。既然如此,那么科学计数法中E就有可能出现负数。所以IEEE 754的真实值,必须加上一个中间数,8位的中间数是127。11位的数是1023。

E可以分为3种情况:

E不全为0或1:这是正常情况,即指数E的计算值减去127(或1023),得到真实值,再将有效数字M前加上第一位的1。

E为全0:这时,浮点数的指数E等于1-127(或者1-1023)即为真实值,有效数字M不再加上第一位的1,而是还原为0.xxxxxx的小数。这样做是为了表示±0,以及接近于 0的很小的数字。

E为全1:如果这时有效数字M全为0,表示±无穷大。

那么如何计算到底如何计算,举个例子:

大端和小端

上面的举例结果为什么会是反过来的,这就关乎于大小端的问题了。大小端命名出自一本名著《格列佛游记》的一个有趣的故事里,有两个国家因为讨论鸡蛋是从大端剥好剥,还是从小端剥好剥的问题就发动了战争。

我们知道,计算机存储的是二进制,但如果要观察二进制来判断一个数就比较麻烦,所以就将二进制转换为十六进制,这样当观察内存的变量时就会方便许多,那么也就衍生出了是应该从哪端看十六进制的大小端问题了。

大端存储就是将低位保存在高地址,高位保存在地址处。

小端存储则与大端相反,低位保存在低地址处,高位保存在高地址处。

 如何用代码判断自己的编译器是大端存储还是小端存储:

#include <stdio.h>
int check_sys()
{
    int i = 1;
    return (*(char *)&i);
}

int main()
{
    int ret = check_sys();
    if(ret == 1)
        printf("小端\n");
    else
        printf("大端\n");
 return 0; 
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值