【Amazing! C】数据的储存

目录

​编辑

一、数据类型的基本归类

1.1 整型

1.2 浮点数 

1.3 构造类型

1.4 指针类型

1.5 空类型

二、整型在内存中的储存

2.1 反码、原码、补码

2.2 大小端介绍

三、浮点型在内存中的存储

3.1 常见的浮点数

3.2 浮点数存储规则

3.2 指数E从内存中取出的三种情况


 一、数据类型的基本归类

1.1 整型

char
    unsigned char
    signed char
short
    unsigned short [int]
    signed short [int]
int 
    unsigned int
    signed int
long
    unsigned long
    signed long [int]

        字符在内存中存储的是字符的ASCII码值,ASCII码值是整型,所以字符类型归类到了整型。

        char 是否是有符号的 signed char?

        C语言标准并没有规定,取决于编译器,在绝大部分编译器中 char == signed char。

1.2 浮点数 

float
double

1.3 构造类型

数组类型
结构体类型    struct
枚举类型      enum
联合类型      union

1.4 指针类型

int * pi;
char * pc;
float * pf;
void * pv;

1.5 空类型

void    表示空类型(无类型)
通常应用于函数的返回类型、函数的参数、指针类型

void test(void)		
test函数没有任何参数,表示test函数不会返回任何值

二、整型在内存中的储存

        当我们写下int a = 10;这行代码时,我们知道,会在栈区开辟一块4个字节空间,并把10储存进去。但是,数据到底是以什么形式怎样去储存呢?

        计算机能够处理的是二进制数据;
        整型和浮点型数据在内存中也都是以二进制的形式进行存储的。

2.1 反码、原码、补码

        整数由3种二进制表示方法:原码、反码、补码。三种方法均有符号位数值位两部分。

        符号位用“0”表示“正”,正的整数,原码、反码、补码相同。

        用“1”表示“负”,负的整数,原码、反码、补码要进行计算。

负整数:

原码:直接将数值按照正负数的形式翻译成二进制就可以得到原码。

反码:原码符号位不变,其他位依次按位取反,得到反码。

补码:反码+1,得到补码。

        整数在内存中储存的是补码的二进制序列。例如:

#include<stdio.h>

int main()
{
    int a = -10;            //4个字节,32个bit位
    //1000 0000 0000 0000 0000 0000 0000 1010    原码
    //1111 1111 1111 1111 1111 1111 1111 0101    反码
    //1111 1111 1111 1111 1111 1111 1111 0110    补码,也是内存中储存的形式
    //按16进制换算    -    每4个二进制位可以换算1个十六进制位
    //例如:1111    -    f(15)
    //  f    f    f    f    f    f    f    6
    //0x ff ff ff f6
    
    unsigned int b = -10;
    //1111 1111 1111 1111 1111 1111 1111 0110    内存中储存的
    //站在无符号整型的角度,所有位都是数值位

    return 0;
}

        在计算机中,整数数值一律用补码来表示和储存。

        优势在于:CPU只有加法器,采用补码储存可以将符号位和数值域统一计算,加法和减法可以统一处理。例如:我们需要做减法:1-1,代码如下:

1-1
1+(-1)
如果用原码计算,是错误的:
0000 0000 0000 0000 0000 0000 0000 0001    -    1
1000 0000 0000 0000 0000 0000 0000 0001    -    -1
相加后得到:
1000 0000 0000 0000 0000 0000 0000 0010    -    -2//err

采用补码计算:
0000 0000 0000 0000 0000 0000 0000 0001
1111 1111 1111 1111 1111 1111 1111 1111
相加后得到:
1 0000 0000 0000 0000 0000 0000 0000 0000  
最高位1丢掉后:
0000 0000 0000 0000 0000 0000 0000 0000    -    0//ok

2.2 大小端介绍

        大端字节存储:把一个数据的低位字节处的数据存放在高地址处,高位字节内容放在低地址处。

        小端字节存储:把一个数据的地位字节处的数据存放在低地址处,高位字节内容放在高地址处。

        我们以 int a = 0x11223344;为例:

        那么,我们可以写一个代码,判断当前计算机的字节序。

int a = 1;

小端存储:
低    ------->    高
   01  00  00  00

大端存储:
低    ------->    高
   00  00  00  01

 a占4个字节,怎么才能拿出4个字节中的1呢?

char* 解引用就可以访问1个字节

代码如下:

#include<stdio.h>

int main()
{
    int a = 1;
    char *p = (char*)&a;
    if(*p == 1)
    {
        printf("小端");
    }
    else
    {
        printf("大端");
    }

    return 0;
}

        我们在上述代码中,将a的地址强制类型转化为字符型指针,使其每次访问1个字节。那么,整型先储存为字符型,再转化为整型,涉及到整型提升,会得到什么样的结果呢?

char a = -1;
1111 1111 1111 1111 1111 1111 1111 1111
char类型只保留8个bit位,所以a中取后8位1111 1111,高位是符号位,后7位是数值位

signed char b = -1;
1111 1111    -    b
unsigned char c = -1;
1111 1111    -    c

printf("a = %d, b = %d, c = %d", a, b, c);    //%d是十进制的形式打印有符号的整数(整型)
a是char类型,要发生整型提升。有符号:整型提升是按照数据类型的符号位来提升的。补的全是1:
//1111 1111 1111 1111 1111 1111 1111 1111
同理,b也一样

c是unsigned char
无符号:整型提升,高位补0
//0000 0000 0000 0000 0000 0000 1111 1111    -    补码,原码与补码相同

         此外,值得注意的是,当整型被强制类型转化为字符型后再做运算时,被强制类型转化的数据会先整型提升再运算。

三、浮点型在内存中的存储

3.1 常见的浮点数

3.14159

1E10        -        1.0*10^10

1.23         -         12.3*10^-1       

        整型的存储形式和浮点数不同,代码证明如下:

#include<stdio.h>

int main()
{
    int n = 9;
    float * pFloat = (float*)&n;
    printf("n的值为:%d\n", n);
    printf("*pFloat的值为:%f\n", *pFloat);
    *pFloat = 9.0;
    printf("num的值为:%d\n", n);
    printf("*pFloat的值为:%f\n", *pFloat);

    return 0;
}

3.2 浮点数存储规则

        任何一个二进制浮点数V都可以表示成下面的形式:

        V = (-1) ^ S * M * 2 ^ E

其中:

        (-1) ^ S:表示符号位,取0或-1;

        M:表示有效数字,大于等于1,小于2;

        2 ^ E:表示指数位,类似于10 ^ (-1)。

        例如:

        十进制的:5.5,转化为二进制为:101.1(1*2^2+0+1*2^0+2^(-1))。转化为上述形式可表示为:(-1)^0*1.011*2^2。

        十进制的:9.0,转化为二进制位:1001.0。转化为上述形式可表示为:(-1)^0*1.001*2^3

IEEE 754规定:

        对于32位的浮点数,最高的1位是符号位S,接下来的8位是指数E,剩下的23位为有效数字M。

        对于64位的浮点数,最高的1位是符号位S,接下来的11位是指数E,剩下的52位为有效数字M。

         对于有效数字M和指数E,还有一些特别的规定:

        前面说过,1<=M<2,也就是说,M可以写成1.xxxxxxx的形式,其中xxxxxx表示小数部分。

        IEEE 754规定,再计算机内部保存M时,默认这个数第1位总是1,因此可以被舍去,只保存后面xxxxxxx部分,比如保存1.01时,只保存01,等读取的时候,再把第一位的1加上去,以此节省1位有效数字。

        至于指数E,情况有些复杂:

        首先,规定,E为一个无符号整数(unsigned int)。

        这意味着,如果E为8位,其取值范围为0-255,如果E为11位,取值范围为0-2047.但是,我们知道科学计数法中,E是可以出现负数的。

        所以IEEE 754规定,存入内存E的真实值必须再加上一个中间数,对于8位的E,这个中间数是127,对于11位的E,这个中间数是1023。

        比如,2^10的E是10,所以保存成32位浮点数时,必须保存成10+127=137,即1000 1001。

float f = 5.5;

储存形式为:(-1)^0*1.011*2^2
S = 0
M = 1.011
E = 2
0 10000001 01100000000000000000000
0100 0000 1011 0000 0000 0000 0000 0000
// 40        b0        00        00

3.2 指数E从内存中取出的三种情况

1. E不全为0或不全为1
浮点数表示:
        指数E的计算值减去127(或1023),得到真实值;再将有效数字M前加上第1位的1。
比如:
        0.5的二进制形式为0.1,由于规定正数部分必须为1,即将小数点右移1位,则为1.0*2^(-1),其阶码为-1+127=126,表示为:0111 1110,

        而尾数1.0去掉整数部分为0,补齐0到23位00000000000000000000000,则其二进制表示形式为:0 01111110 00000000000000000000000。

2. E全为0

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

3. E全为1

        如果有效数字M全为0,表示正负无穷大,正负取决于符号位。

        所以,我们可以理解刚才的例子:

#include<stdio.h>

int main()
{
	int n = 9;
	//0000 0000 0000 0000 0000 0000 0000 1001
	float* pFloat = (float*)&n;//因为是int*,所以不能直接赋给pFloat类型,需要强制转化,所以现在pFloat指向了n的4个字节

	printf("n的值为:%d\n", n);//9
	printf("*pFloat的值为:%f\n", *pFloat);//0.000000
	//访问浮点型并拿出来,因此内存理解为
	//0 00000000 00000000000000000001001
	//S		E				M
	//E全0	
	//E=-126
	//M=0.00000000000000000001001
	//S=0
	//(-1)^0*0.00000000000000000001001*2^(-126)
	//float只能打印小数点后6位
	*pFloat = 9.0;
	//9.0
	//1001.0
	//(-1)^0*1.001*2^3
	//往内存中存放
	//0 10000010 00100000000000000000000
	//拿出来
	printf("num的值为:%d\n", n);
	//0100 0001 0001 0000 0000 0000 0000 0000// -> 1091567616
	printf("*pFloat的值为:%f\n", *pFloat);//9.000000
	
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值