主页 > 创业  > 

数据的存储(C语言)

数据的存储(C语言)
数据类型:

要了解数据是如何存储的,我们就得先知道C语言中的数据类型

基本数据类型

基本数据类型,也就是C语言内置类型:

char                 -> 字符型

short                -> 短整型

int                     -> 整型

long                  -> 长整型

long long          -> 更长的整型

float                   -> 单精度浮点型

double               -> 双精度浮点型

数据类型的大小

C语言中可用sizeof操作符来计算数据类型在内存中所占空间的大小,单位是字节

#include <stdio.h> int main() { printf("%zd\n", sizeof(char)); //char大小 printf("%zd\n", sizeof(short));//short的大小 printf("%zd\n", sizeof(int));//int的大小 printf("%zd\n", sizeof(long));//long的大小 printf("%zd\n", sizeof(long long));//long long 的大小 printf("%zd\n", sizeof(float));//float的大小 printf("%zd\n", sizeof(double));//double的大小 return 0; } 数据类型的意义

1、类型决定了要开辟空间的大小

2、类型的大小决定了空间的使用范围

3、类型决定了如何看待内存空间的视角

类型的基本归类 整型家族:

char    

        unsignde char  

        signed char

short

        unsigned short

        signed short

int 

        unsigned int

        signed int

long

        unsigned long

        signed long

long long

        unsigned long long

        signed long long

注:char类型也是整型家族的一员

       char类型在内存中存储的时候,存的是其对应的ASICC码值

        unsigned 表示 无符号数

        signed 表示 有符号数

浮点数家族:

float   单精度浮点型

double  双精度浮点型

构造类型:

构造类型也叫自定义类型

是我们自己所创建的类型

数组类型

结构体类型 struct

联合类型  union枚举类型  enum

指针类型:

指针类型分类:

整型指针:int*

字符指针:char*

浮点型指针:float*

空类型指针:void*

........

注:空类型的指针可以接收任意类型的指针数据

空类型

void 空类型(无类型)

用途:

        函数的返回类型

        函数的参数

        指针类型

        ......

整型在内存中的存储

整型家族在内存中是以二进制的补码进行存储

那么要了解它的存储,

我们首先要了解清楚,原码、反码、补码的概念

原码、反码、补码

为什么存的是补码?

因为使用补码,可以将符号位和数值域统一处理

同时,加法和减法也可以统一处理(CPU只有加法器)

此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路

原码 转 补码

原码 —> 反码 —> 补码 (原码转补码)

原码:将整数直接转化成二进制,这个二进制数就是它的原码

反码:将原码的符号位不变,其它位按位取反得到反码

补码:给反码 +1 得到补码

注:转化二进制数的最高位是符号位,其中 0 表示正数,1 表示负数

eg:

#include <stdio.h> int main() { int a = 10; //定义一个整型变量,在内存中开辟4字节的空间 //变量名为a,这个空间里面存储10 //那么数字10是如何存储的呢? // 整型家族在存储的时候存的是二进制的补码 // 先将数字化成二进制 得到原码 // 00000000 00000000 00000000 00001010 --》原码 // 再将原码按符号位不变,其它位按位取反得到反码(最高位是符号位,0表示正数,1表示负数) // 01111111 11111111 11111111 11110101 --》反码 //再让反码+1得到补码 // 01111111 11111111 11111111 11110110 --》补码 //而在内存中存的就是二进制的补码 printf("%d\n",a); return 0; } 补码转原码

方法1: 倒着推回去

补码  —> 反码 —> 原码

先让补码 -1 得到反码,

再让反码符号位不变其它位按位取反得到原码

方法2: 重新按照原码转补码的步骤走一遍

先让补码符号位不变其它位按位取反 得到一个二进制序列

再让这个二进制序列 +1 得到原码

eg:

#include <stdio.h> int main() { int a = 10; // 定义一个整型变量a // a的原码:00000000 00000000 00000000 00001010 // a的反码:01111111 11111111 11111111 11110101 // a的补码:01111111 11111111 11111111 11110110 printf("%d\n", a); //打印a //因为在内存中存的是补码,而我们取出来的时候是要用原码, //所以 要将补码 转回 成原码 // 方法1:倒着推回去 // a的补码:01111111 11111111 11111111 11110110 // 让补码 -1 得到反码 // a的反码:01111111 11111111 11111111 11110101 // 让反码符号位不变,其它位按位取反得到原码 // a的原码:00000000 00000000 00000000 00001010 // 最后将原码以 %d(十进制数)的形式打印出来 //方法2:重新按照原码转补码走一遍 // a的补码:01111111 11111111 11111111 11110110 // 让补码符号位不变,其它位按位取反 得到一个二进制序列 //二进制序列:00000000 00000000 00000000 00001001 //再让这个二进制序列 +1 得到原码 // a的原码:00000000 00000000 00000000 00001010 return 0; } 大小端字节序存储

每个机器的存储模式不同,

二进制的补码在存储时 有的机器是从前往后存储,而有的机器是从后王前存储

由于存储顺序不同,就产生了大小端的概念

当前机器是从后往前存储的

大端存储:数据的低权值位保存在内存的高地址处,数据的高权值位保存在内存的低地址处

小端存储:数据的低权值位保存在内存的低地址处,数据的高权值位保存在内存的高地址处

(小端口诀:小小小(低权值位,低地址,小端))

例题:判断当前机器是大端字节序存储,还是小端字节序存储

#include <stdio.h> int main() { int a = 1; //原码:00000000 00000000 00000000 00000001 //反码:01111111 11111111 11111111 11111110 //补码:01111111 11111111 11111111 11111111 //显示的时候是用十六进制显示 //原码:00000000 00000000 00000000 00000001 // 十六进制显示:00 00 00 01 // 小端存储:10 00 00 00 // 大端存储:00 00 00 01 //将a的第一个字节的地址给b // *b取到第一个字节的内容,若为1 是小端,若为0 是大端 char* b = &a; if (*b == 1) { printf("小端\n"); } else { printf("大端\n"); } return 0; } 例题: //第一题 #include <stdio.h> int main() { char a = -1; //原码:10000001 //反码:11111110 //补码:11111111 signed char b = -1; //有符号的char取出来的时候还是将补码转为原码 // 取出来的原码:10000001 unsigned char c = -1; //无符号char 取出来的时候,认为补码就是原码 //取出来的原码:11111111 printf("a=%d b=%d c=%d\n", a, b, c);// -1 -1 255 return 0; } //第二题 #include <stdio.h> int main() { char a = -128; // 原码:10000000 // 反码:01111111 // 补码:10000000 printf("%u\n", a);//非常大的一个数字 //在取的时候是用无符号整数 //认为补码就是原码 // 10000000 00000000 00000000 00000000 return 0; } //第三题 #include <stdio.h> int main() { char a = 128; // 正数的原反补相同 // 补码: 10000000 printf("%u\n", a);//2^32 //在取得时候 进行整型提升 //10000000 00000000 00000000 00000000 return 0; } //第四题 #include <stdio.h> int main() { int i = -20; //原码:10000000 00000000 00000000 00010100 //反码:11111111 11111111 11111111 11101011 //补码:11111111 11111111 11111111 11101100 unsigned int j = 10; //原反补相同 //补码:00000000 00000000 00000000 00001010 //补码:11111111 11111111 11111111 11101100 // 11111111 11111111 11111111 11110110 //让他们的补码相加,在取的时候是%d 有符号的,所以结果是-10 // 10000000 00000000 00000000 00001001 // 10000000 00000000 00000000 00001010 -10 printf("%d\n", i + j); return 0; } //第五题 #include <stdio.h> int main() { unsigned int i = 0; for (i = 9; i >= 0; i--) { printf("%u ", i); } // 9 8 7 6 5 4 3 2 1 0 //当i--变成-1的时候-1的二进制序列为:10000001 //但因为i是无符号整数,所以在取的时候,会将符号位当做是数位计算进去 //在打印的时候会发生整型提升,所以会发生死循环! // 10000001 0000000 00000000 00000000 return 0; } //第六题 #include <stdio.h> int main() { char a[1000]; int i = 0; for (i = 0; i < 1000; i++) { a[i] = -1 - i; } // -1 ..... -127 128 ...... 0 // 127+128=255 //strlen函数遇到’'\0'才停止 而‘\0'的ASICC值是 0 所以遇到0就结束 printf("%d\n", strlen(a)); return 0; } //第七题 #include <stdio.h> unsigned char i = 0; int main() { // 00000000 // 00000001 // ....... // 11111111 // ...... // 10000000 //死循环 for (i = 0; i <= 255; i++) { printf("hello world\n"); } return 0; } 浮点型在内存中存储

  浮点家族:float、double、long double 类型

浮点数存储的一个例子:

#include <stdio.h> int main() { int n = 9; float* p = (float*)&n; printf("%d\n", n);//9 printf("%f\n", *p);//0.000000 *p = 9.0; printf("n的值为:%d\n", n);//1091567616 printf("*p的值为:%f\n", *p);//9.000000 return 0; }

看完代码结果跟我们想的完全不一样,为什么不一样呢,是因为浮点数的存储,

以下让我们仔细了解一下浮点数到底是如何存储的

浮点数的存储规则

根据国际标准IEEE(电气和电子工程协会) 754,任意一个二进制浮点数V可以表示成下面的形式:

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

(-1)^S表示符号位,当S=0,V为正数;当S=1,V为负数。

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

2^E表示指数位。

举个例子:

浮点数的 5.0,转成二进制:101.0,相当于:1.01*2^2

按照IEEE规定则可以看成:(-1)^0  *  1.01  * 2^2

按照公式:S=0,M=1.01,E=2

存储时如何放入内存的呢?

IEEE 754 规定:

1、32位的浮点数的存储:

        最高的1位是符号位S,接着的8位是指数E,剩下的32位是有效数字M

2、 64位浮点数的存储

        最高的1位是符号位S,接着的11位是指数E,剩下的52位是有效数字M

 

IEEE 754,对有效数字M,和指数E 有些特别的规定:

1、对于有效数字M:

因为 1≤M<2  所以M总是 1.xxxx...  的数,

因此1 可以被舍弃,我们在存储的时候只存储M的xxxx...部分,

等到读取的时候在xxx...前面把1加上去

好处:舍弃1这样可以存储24位有效数字

2、对于指数E:

首先E为一个无符号整数:

因为E是科学技术法的指数,所以会出现负数的情况,但是E是一个无符号整数,

所以IEEE 754 规定:E在存入内存时,其真实值必须加上一个中间数

对于8位的E 中间数是 127,对于11位的E 中间数是 1023

eg: 2^10  中 指数E为10,将它保存成32位浮点数,E=10+127=137

      存入内存中:10001001

然而指数E从内存中取出又分为三种情况:

取E的三种情况:

1、E不全为0或不全为1:

        此时取的时候 将E减去中间值(127或1023)得到真实值,

        再将有效数字M前面加上第一位的1

        eg:浮点数0.5 (1/2) 二进制为:0.1 

        转化为:(-1)*0 * 1.0 * 2^-1

         在存储时 给8位E加上中间数127,舍弃M第一位的1,不够位的补0

        S=0,E=-1+127=126,M=1.0

        则存储起来的二进制为:0 01111110 00000000000000000000000

2、E为全0:

        此时取的时候,指数E=1-127或者E=1-1023,即为真实值

        有效数字M不再加上第一位的1,而是还原为 0.xxxx... 的小数

3、E为全1:

        如果有效数字M全为0 ,表示无穷大 (±无穷大)(正负号取决于S)

注:由于浮点数跟整数的存取方式不同,用不同的方式取的时候结果也大不相同

此时我们再回过头来解释刚刚的例题

#include <stdio.h> int main() { //由于浮点数跟整数的存取方式不同,用不同的方式取的时候结果也大不相同 int n = 9;//n是整数,存在原反补问题 //原码:00000000 00000000 00000000 00001001 //正数的原码、反码、补码相同 //补码:00000000 00000000 00000000 00001001 // 内存中存的就是整数的补码 float* p = (float*)&n; //将整型的内存空间强制转化成浮点型 printf("n = %d\n", n);//9 // 用%d读取的时候,打印的是有符号十进制整数 // 由于n是正数,原码、反码、补码都相同 // 所以用%d打印 结果就是9 printf("%*p = %f\n", *p);//0.000000 //用%f读取的时候,打印的是浮点数, // 而这片空间里面存储的是:00000000 00000000 00000000 00001001 // 用浮点数的存储规则取出来 // 0 00000000 00000000000000000001001 // S E M // 在取的时候,S为0表示正数,E全为0的情况 有效数字M不加第一位的1 //而在打印的时候%f只能精确到有效数字后六位 // 所以结果为:0.000000 *p = 9.0; //在p指针指向的空间n里面放入浮点数字9.0 //9.0的二进制数:1001.0 -- 1.001*2^3 //由浮点数的存储规则可知: //(-1)^0 * 1.001 * 2^3 //S=0,E=3,M=1.001 //在存储的时候让E加上中间值,让M舍弃第一位的1 //S=0,E=3+127=130,M=001 //存入到内存的二进制数,不够位数的补0 //0 10000010 00100000000000000000000 printf("n = %d\n", n);//1091567616 //用%d读取的时候是以整数的形式读取内存中存入的二进制数 //0 10000010 00100000000000000000000 //将内存中的二进制数读取为整数结果是:1091567616 printf("n = %f\n", *p);//9.000000 /*用%f读取的时候读取的是浮点数 而内存中本身就存入的是浮点数 打印的结果就是它本身:9.000000*/ return 0; }
标签:

数据的存储(C语言)由讯客互联创业栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“数据的存储(C语言)