数据的存储 详解

1 .数据类型的介绍

   1.1类型的基本归类

--------------------------------------------------------------------------------------------------------------------------------

基本的数据类型:

char       //字符数据类型(整形家族)
short      //短整型
int        //整型
long       //长整型
long long  //长长(更长的)整型 (c99)
float      //单精度浮点数
double     //双精度浮点数
         
    printf("%dn", sizeof(char));	    //1个字节    
	
    printf("%dn", sizeof(short));      //2个字节    
	
    printf("%dn", sizeof(int));        //4个字节  (32位机器或64位机器)早期为(16位机器)2个 字节   
	
    printf("%dn", sizeof(long));       //4个字节     
	
    printf("%dn", sizeof(long long));  //8个字节  
	
    printf("%dn", sizeof(float));      //4个字节  
	
    printf("%dn", sizeof(double));     //8个字节  

---------------------------------------------------------------------------------------------------------------------------------

                                                这 是 一 些 扩 展 大 家 可 以 看 看

有没有字符串类型呢?

答案是  有的

字符串是字符的序列,基本就是一组单词,是Phthon中常用的数据类型,可以用单引号(' ')和双引号(" ")来定义 (创建)字符串。

//比如可以这样
str = "My name is 9527 ";

str1 = 'HELLO WORLD!';

str2 ="""because of you""";//对了也可以用三引号(""" """)和(''' ''')
------------------------------------------------------------
print("%s" % str); //输出为 My name is 9527

print("%s" % str1);//输出为 HELLO WORLD!

print("%s" % str2);//输出为 because of you

不同的数据类型内会导致存储时和访问时有不同的差异 比如:浮点型就是一个典例(后面会给大家讲到 嘿嘿)

做点小扩展(摘自 cppreference.com)

  • 浮点类型
  • 实际浮点类型:float double ,long double
  • 十进制实数浮点类型:_Decimal32,_Decimal64,_Decimal128
(自 C23 起)
  • 复杂类型:float_Complex,double_Complex,long double_Complex
  • 虚数类型:float_Imaginary,double_Imaginary,long double_Imaginary
(自C99起)

可以简单了解一下 嘻嘻嘻

其次就是 布尔类型 (_Bool) 是C语言中专门用来表示 真 / 假 的一种类型(c++中也有哦)

c99标准才开始引入原因是因为 在早期 C语言都是用0表示假 非0为真 后期才开始有了这样

给大家演示一下:

   //c99才开始引入
#include <stdio.h>
#include <stdbool.h>//这里需要引用头文件
 int main()
{

     _Bool flag = true;//true为真false为假
	
     if (flag)
	{
		printf("Oh yeah! , you do it!");
	}

   return 0;
}

_Bool 没有什么好神奇的 本质是对 int  的一种重命名 你看这就很好的说明了 并且平常我们看到的代码也很少有用到_Bool类型的 即使 c99 中引入了 引用得不是很广泛有些编译器甚至不支持 当然你想用也是可以的 哈哈哈哈

   #define bool  _Bool
   
   #define false 0 //类似于int false = 0;
   
   #define true  1//类似于int true = 0;

---------------------------------------------------------------------------------------------------------------------------------

                                             “ 家族  ”类型

1. 接下来是我们的整型家族

    char :
		unsigned char
		signed char
	
    short :
		unsigned short  //short等价于signed short
		signed short
	
    int:
		unsigned int    //int等价于signed int
		signed int
	
    long:
	    unsigned long   //long等价于signed long
    	signed long

char类型 之所以归于整型家族中是因为其字符有与其相对应的 ASCLL码值

对于 char类型 是不一样的 char 到底等不等同于 signed char 是取决于编译器的不同的

但是一般来讲是 等同

需要注意 有符号 无符号 在打印是也要求的

    //注意:%d 是按照有符号数来打印的
    //%u 才是按照无符号来打印的

    unsigned int num = -10;
	printf("%d", num);       //打印为 -10  

    int num1 = -10;
	printf("%u", num1);      //打印出来为4294967286

2 浮点数家族

float
double

除了这些类型之外还有 构造类型 (就是自己构造的类型)

  • 派生类型
(自C11起)

对于上面列出的每个类型,可能存在其类型的几个限定版本,对应于一个,两个或全部三个const,volatilestrict限定符的组合(在限定符的语义允许的情况下)。(摘自 cppreference.com)

可以提的一点就是还有一个 void类型 他表示一种空类型(应用于 函数的返回的类型、函数的参数、函数的类型)。

void function(void)
{
    void *tmp;

}

2.    整型在数据中的存储

变量的创建是需要在栈区创建空间的 而不同的类型决定了创建的空间的大小

2.1 原码、反码、补码

在了解 原 反 补 之间的关系前 我们要知道一个点就是整数在内存存储的补码

正整数 的 原 反 补 是相同的

负整数 的 原 反 补 之间的关系可以这样解释

int a= -12;

//1 0000000 00000000 00000000 00001100 (原码)一个字节八个bit位
//1 1111111 11111111 11111111 11110011 (反码)按位取反符号位不变
//1 1111111 11111111 11111111 11110100 (补码)取反后加一

//前面的1为符号位 1表示其为负数 0表示其为正数

int b= 12;

//0 0000000 00000000 00000000 00001100(原 反 补)

在计算机系统中,数值一律用补码来表示和存储。原因在于使用,可以将符号位和数值域统一处理

同时,加法和减法也可以统一处理(CPU只有加法器)此外,补码与原码相互转换其运算过程是相同的,不需要额外的硬件电路。

用补码有什么效果好处是什么?

大家可以这样看看

int a = 1;
int b =-1;

//00000000 00000000 00000000 00000001 (a的原码,反码,补码)

//10000000 00000000 00000000 00000001 (b的原码)
//11111111 11111111 11111111 11111110 (b的反码)
//11111111 11111111 11111111 11111111 (b的补码)

//如果此时我们计算 a+b 使用原码

//得到的结果就是这样的
//11111111 11111111 11111111 11111110 显然这个是不等于0

//如果我们用到补码来计算情况就是这样的了
//1 00000000 00000000 00000000 00000000此时前面的1就溢出了 最后得到就是
//00000000 00000000 00000000 00000000 此时得到结果就是0了

2.2 大小端

大段 (存储) 模式 数据的低位保存在内存的高地址,而数据的高位,保存在内存的低地址

小段 (存储)模式 数据的低位保存在内存的低地址,而数据的高位,保存在内存的高地址

其实通俗来讲的话 可以这样理解: 小段(存储)模式下内存存储的字节序是相反的,大段就是与之相反的。

大家来看:

// 大家看
// 10的补码 00000000 00000000 00000000 00001100 

//4个字节为1个16进制

//0000 0000 | 0000 0000 | 0000 0000 | 0000 1100 
//  0    0  |   0    0  |   0    0  |   0   a(10)
   高字节  <----------------------------低字节
//所以按照 小端存储 的话 就是反着存的
//拿进去就是这样的 

//0a 00 00 00

 这就是小段的存储 大家看懂了吗?大段就是与之相反的 嘻嘻

最后补一句 只要一个数值在内存存储的大小超过一个字节 那么他在内存中的字节存储就有顺序

就好像char类型就没有字节序(大小端)的区别

除此之外 大家还需要注意数据在进行赋值时的一些原理 (因为他们很可能会发生 整型提升算数转换

就好像是这样:



   char x = -1;          //大家注意看这里 -1为一个整型(32个bit位)现在要放在一个字符型(8bit 
  位)的变量中那么就需要发生截断 

  //11111111 11111111 11111111 11111111
  //                           11111111(截断后就是这样了前面的1就没有了)
   

   signed char y = -1;  //在VS中char等同于signed char 所以截断和第一个一样的

   unsigned char z =-1; //这里也是一样的道理 无符号并不会有影响 他依旧只能存储8个bit位

整型提升内就是以他的符号位的数字往前补1或0(这里就不做细讲了)

//如果不理解的话大家可以看看这样

//在printf("%d %d %d",x,y,z);   这个时候之前的char 、signed char 、unsigned char就开始发挥他们的作用了

//之前不是 x 被 char 截断的后变成了  11111111

%d是要以有符号的整型打印出来前面也提到过了
所以呢这个时候 就会发生整型提升:
//11111111
//11111111 11111111 11111111 11111111(因为截断后的x是有符号的并且为 1 所以就补 1 )
这个时候内 这还是补码 要转换成原码来打印出来
因为-1的补码为全1 (大家可以记一下)所以这个时候打印出来就是 -1

unsigned char截断的z就会在前面补0(因为它是无符号的就补0)
//00000000 00000000 00000000 11111111
%d打印是以有符号的方式打印出来 所以它(%d)就会认为0 是他的符号位 因为0为符号位为正数 
原、反、补码相同 此时的虽为补码 原反补相同就直接按照
//00000000 00000000 00000000 11111111
打印出来 就是这样的 嘿嘿

3. 浮点数在内存中的存储(与整数是不一样的哦)

浮点数家族:float 、double 、long double

其中大家可以了解一下这个 1E6 这也是一个浮点数 1E6 =1*10^6

(浮点数后边不加 f 默认为double类型 加后改为float类型)

---------------------------------------------------------------------------------------------------------------------------------

大家来看一下浮点数的存储规则:

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

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

 1.  (-1)^S表示的是符号位,当S为0时 V 为正数 当S为1,V为负数

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

 3.  2^E表示指数位

---------------------------------------------------------------------------------------------------------------------------------

   //大家来这样看
好比 6.5-->110.1(二进制)  0.5=2^-1所以 0.5-->0.1(二进制)

// 110.1=1.101*2^2  这里乘以二的二次方 就好比 9527=9.527*10^3 科学计数法

//6.5为正数 所以 (-1)^S 这里的S就等于0;
//有效数字M就为1.101
//指数E就是2

接下来 就是这些数应该如何放进去:(画得不太好 嘻嘻)

1. S 的存储即1或者0放进去(浮点数不存在原反补的概念)2

2. 因为之前 说到 1<= M <2 所以 可以表示成 1点几 (1.??????) 所以在存储 时就只会存储小数点后面的数 这样的话拿出来使用只需要在 前面加 1 就可以了 并且这样还能节省一个空间也就可以多存储一位了 怎么样 是不是很 女少

3. 存储 E时情况就比较复杂了 分为以下几种:

首先大家要知道E无符号整型(unsigned int)

如果计算出来 E 的值需要加一个中间数 ,8位的情况下加的是127,11位的情况下加的是1023

好比 :现在 计算出来为-1 那么存进去时就应该是 -1+127=126 存进去

10的话 就是10+127=137 大家明白了吗 (还有一点 8位情况下 E的取值范围是0~255 这里的范围是计算的 E 加上中间值127后的范围 这样说大家理解嘛?)

在取出E时又分为以下的几种情况:

1 .E不全为1或不全为0的时候:

这个时候就只需要把 的值取出来减去一个中间值即可

2. E为0时:

E为0就意味着之前计算出来的 为-127 大家想想一个指数-127 (2^-127)得多小啊

所以这个时候 直接拿出来就等于 1-127 或 1-1023 此时有效数字 就不再加1了而是直接变成0.????(大家可以了解一下)

    (-1)^S*1.???*2^-127
//这样的数不就是一个趋近于0的很小很小的数了嘛

3.E为全1时:

// 1111 1111 这个计算出来为255 说明计算出来的 E 就应该为 128 
// 想想一个数 2^128是不一个很大的数目

//到这里大家就已经看见 E 为全0,E 为全1 就是两个相对的极端 

给大家演示一下下:

int n =12;
float *p=(float*)&n; 
printf("%fn",*p);   //注意这里是以整型放进去 然后以浮点数的形式打印出来

//00000000 00000000 00000000 00001100
%f去识别时
//0 00000000 00000000000000000001100
  S     E               M

//这时E全为0 所以M就不用加上1了所以这个拿出来就是

//(-1)^0 * 0.00000000000000000001100 * 2^-126 得到就是一个很小很小的数

%f %lf 都是默认为输出小数点后六 这里显然后六位 全为0 输出自然而然就是 0.000000
//再给大家演示一下 存储的过程

延续刚刚的代码
*p = 12.0;
printf("%f",*p);  //这里就是浮点数放进去浮点数拿出来了
printf("%d",*p);

   12.0-->十进制 S=0
   1100.0-->二进制
   1.100*2^3-->E=3,M=0.100
  
//0 10000010 100 00000000000000000000(后面不够补0)
 //完整来看 0 10000010 10000000000000000000000
存储在内存中时 大家这样来看
// 0100 0001 0100 0000 0000 0000 0000 0000
// 4    1    4    0    0    0    0    0  大家想想浮点数有大小端之分嘛

 很明显是 有的

图中 
printf("%d",n);   //这里的n打印出来是 1094713344 为什么内 我给大家解释一下啊

此时n的地址中的内容已经发生了改变
// 0100 0001 0100 0000 0000 0000 0000 0000 如果把这个放到计算器中

 大家看 是不是一样的 嘿嘿

---------------------------------------------------------------------------------------------------------------------------------

到这里内 数据的存储 就讲完啦 感谢大家看到这里 谢谢啦 (第一篇博客 有些不好的地方麻烦各位大牛指正)

那就这样吧! 祝大家都能进入自己喜欢的大厂! 加油

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>