栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C语言数据在内存中的存储

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C语言数据在内存中的存储

数据在内存中的存储 数据类型

数据类型是数据的基本属性,描述了数据的存储格式和运算规则,不同类型的数据占用着不同大小的存储空间,有着不同的存储格式,能支持的运算和操作也有所不同,因此,在程序中的所有数据,都必须指定其数据类型

数据类型的分类
  • 基本类型

    • 整型数据("[]"里面的可以省略)

      short
          [signed] short [int]  //在定义一个short类型时,默认为有符号短整型
          unsigned short [int]
      int 
          [signed] int          //在定义一个int类型时,默认为有符号整型
          unsigned int     
      long
          [signed] long [int]   //在定义一个长整型时,默认为有符号长整型
          unsigned long [int]
      
    • 字符型数据

      char
          [signed] char         //在定义一个char类型时,默认为有符号字符型 数值范围:-128~127
          unsigned char         //数值范围:0~255
      

  • 浮点型数据

    float
    double
    long double
    
  • 构造类型

    • 数组 ([])
    • 结构体(struct)
    • 共用体(union)
    • 枚举类型(enum)
  • 指针类型(*)

  • 空类型(void)

    • 空类型是 void 类型,它主要用于函数的返回类型,函数的参数,指针类型
整型数据在内存中的存储 原码、反码、补码
  • 二进制:

    • 二级制是计算机技术中广泛采用的一种数制,采用0和1两个数码来表示数,基数为2,进位规则是“逢二进一”,借位规则是“借一当二”。

    • 计算机中所有的数据、指令以及符号等都是用二进制代码表示的,一个数在计算机内被表示的二进制形式称为机器数, 该数称为这个机器数的真值。机器数具有以下特点:

      • 机器数有固定的位数,因此机器数能够表示的数是在一定范围内的,超过这个范围就会产生“溢出”,因此计算机内部整型数据同样只能表示一定范围内的整数。

      • 有符号整数:机器数中采用符号位(一般是最高位,取0或1表示)表示整数的正或负。

        无符号整数:没有采用符号位。C语言中定义了有符号整数和无符号整数

      • 不管是正整数、负整数、无符号整数,在计算机中都是以补码的形式存储和使用。

有符号整数和无符号整数
  • 有符号整数用最高位表示符号(正或负),其余位表示数值大小

  • 无符号整数所有位都用于表示数值的大小

    以一个字节(8位)为例(char类型):

    无符号整数的最大值为256; (256)10 = (1111 1111)2

    有符号整数的最大值为127; (+127)10 = (0111 1111)2

    这是因为有符号数中的最高位被移去表示符号位。

无符号整数在计算机内部表示

符号整数直接用对应的二进制数表示,所有的位都用于直接表示数的大小;

有符号整数在计算机内部表示
  • 有符号整数在计算机内部用补码表示
  • 正数的原码、反码、补码均相同

原码:原码的最高位为符号位,0表示正,1表示负,其余位数表示该数的绝对值。通常用 [X]原 表示X的原码。

  • 因为 (17)10 = (10001)2,那么 [+17]原 = 00010001。
  • 因为 (39)10 = (100111)2,那么 [-39]原 = 10100111。
  • 根据 [X]原 能表示的整数范围公式,可以计算一个字节是原码的表示范围是**[-127,127]**

反码:正数的反码和原码相同,负数的原码就是把其原码除符号位外的各位按位取反(即0变1,1变0)。通常用 [X]反 表示X的反码。

  • [+45]反 = [+45]原 = 00101101
  • 因为 [-32]原 = 10100000,那么 [-32]反 = 11011111
  • 根据 [X]反 能表示的整数范围公式,可以计算一个字节是反码的表示范围是**[-127,127]**

补码:正数的补码与原码相同,负数的补码是在其反码的最低有效位上加1。通常用 [X]补 表示X的补码。

  • [+14]补 = [+14]补 = 00001110

  • 因为 [-36]原 = 10100100,而 [-36]反 = 11011011,所以 [-36]补 = 11011100

  • 根据 [X]补 能表示的整数范围公式,可以计算一个字节是反码的表示范围是**[-128,127]**

  • 补码的意义:采用补码可以将减法变为加法,省去减法器;又由于乘除可通过移位和加减来实现,于是使四则运算在计算机中能转化为对补码的简单移位和相加,大大简化计算机运算部件的电路设计,此外,补码与原码相互转换,其运算过程是相同的。

    • 公式如下:[ X+Y ]补 = [ X ]补 + [ Y ]补 和 [ X-Y ]补 = [ X ]补 + [ -Y ]补
整型提升

具体在操作符章节讲解

大小端
  • 首先我们来看整型数据在编译器下的内存是怎么存储的?

    #include 
    int main()
    {
    	int a = 10;    //10的补码:00000000000000000000000000001010  转换为十六进制:0000000a
    	int b = -10;   //-10的补码:11111111111111111111111111110110 转换为十六进制:fffffff6
    	return 0;                  
    }
    

  • 我们发现,在编译器运行时,存储在内存里面的数据是倒着存储,这是为什么呢?

  • 什么是大端小端?

    • 大端(存储)模式,是指数据的低位保存在内存的高地址,而数据的高位,保存在内存的低地址中;
    • 小端(存储)模式,是指数据的低位保存在内存的低地址,而数据的高位,保存在内存的高地址中;
  • 为什么有大小端?

    • 在计算机系统中,我们是以字节为单位的,每个地址单元都对应着一个字节,一个字节为8个bit。但是在C语言中除了8bit的char类型以外,还有16bit的short类型,32bit的long类型,另外,对于数位大于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如何将多个字节安排的问题,因此就导致了大端存储模式和小端存储模式。
  • 设计一个小程序来判断当前机器的字节序

    #include 
    int main()
    {
    	int a = 1;
    	char* p = (char*)&a;
    	if (*p == 1)
    	{
    		printf("小端");
    	}
    	else
    	{
    		printf("大端");
    	}
    	return 0;
    }
    
浮点型数据在内存中的存储
  • 根据国际标准IEEE(电气和电子工程协会)754,对任意一个二进制浮点数V可以表示成下面的形式;

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

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

      • M 为有效数字,且其范围:1<=M<2;

      • 2 ^ E 表示指数位

      • 例:

        (5.0)10 =(101.0)2,相当于(-1)^0*1.010*2^2 即1.01*2^2;可以得出:S=0;M=1.01;E=2。

        (-5.0)10 =(-101.0)2,相当于(-1)^1*1.010*2^2 即-1.01*2^2;可以得出:S=1;M=1.01;E=2。

  • IEEE 754 规定

    • 对于32位的浮点数(即float类型),最高位是符号位S,占用一个bit位;接着是指数E,占用8个bit位;剩下的是有效数字E,占用23个bit位。

  • 对于64位的浮点数(即double类型),最高位是符号位S,占用一个bit位;接着是指数E,占用11个bit位;剩下的是有效数字E,占用52个bit位。

  • IEEE 754对有效数字M和指数E,还有一些特别规定

    • 对于有效数字M:

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

      IEEE 754规定,在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的xxxxxx部分

      例如:保存1.01时,只保存01,等到读取的时候,再把第1位加上去,这样做的目的是,节省1位有效数字。

      以32位浮点数为例,留给M只有23位,将第一位的1舍去之后,就可以保存24位有效数字了。

    • 对于指数E:

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

        例如:2^10中E是10,所以保存成32浮点数时,必须保存为10+127=137,即10001001;

      • 指数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,表示±无穷大(正负取决于符号位)

  • 例:

  • 例1:

    int main()
    {
    	float f = 5.5f;
    	//转换为二进制:101.1
    	//表示成:(-1)^0 * 1.011 * 2^2   S=0;M=1.011;E=2;
    	//存入内存:S存入0   M存入011   E存入2+127=129
    	//内存区域:0 10000001 01100000000000000000000 (二进制)
    	//内存区域:40 b0 00 00  (十六进制)而这个就是编译器下内存中展示的
    	return 0;
    }
    

  • 例2:

  • #include 
    int main()
    {
    	int n = 9;
    	float* pFloat = (float*)&n;
    	printf("n的值为:%dn", n);
    	printf("pFloat的值为:%fn", *pFloat);
    
    	*pFloat = 9.0;
    	printf("num的值为:%dn", n);
    	printf("*pFloat的值为:%fn", *pFloat);
    	return 0;
    }//编译结果:
    //n的值为:9
    //pFloat的值为:0.000000
    //num的值为:1091567616
    //*pFloat的值为:9.0
    
    • 为什么会出现这样的结果?
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1025355.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号