一、 数据的类型

1、C语言中的数据类型

可以分为以下几种:

深度剖析数据在内存中的存储_字节序

2、在C语言中

char类型占1个字节

short类型占2个字节

int类型占4个字节

long类型没有固定具体的大小,所占空间>=int类型所占空间

long long类型占8个字节

float类型占4个字节

double类型占8个字节……


3、类型的意义

      (1)这个(种)类型开辟空间的大小,开辟空间的大小决定了定义的数据的存储的范围及使用的范围。

      (2)看待开辟内存空间的视角不同。

4、数据的类型

可以划分为五种,分别为:整形类型、浮点类型、指针类型、构造类型、空类型。

整形类型:

深度剖析数据在内存中的存储_浮点数_02

      char类型虽然是字符类型,但在存储char定义的变量时,存储的是变量的ASCII码值,ASCII码值是整数,所以char类型属于整形变量的范畴。

      char类型没有具体的规定它是signed char 或 unsigned char,它的类型是什么,是取决于编译器的,不同的编译器对char的类型有着不同的规定。

浮点类型:

深度剖析数据在内存中的存储_字节序_03

构造类型:

深度剖析数据在内存中的存储_字节序_04

大家可能对数组也是构造类型感觉到奇怪,我们定义一个数组,int arr[10] = {0}; 其中,arr是数组的变量名,而int [10]是这个数组的类型,如果将[]内的值从10修改为5,那这个数组的类型就变成了int [5],数组类型发生了改变,所以说数组也是一种构造类型。

指针类型:

深度剖析数据在内存中的存储_字节序_05

空类型:

深度剖析数据在内存中的存储_浮点数_06


二、 整数在内存中的存储

任何一个变量存储在内存中都需要开辟一块空间来存放,又因为计算机只认识机器语言,机器语言就是二进制的0和1组成的语言,所以想让计算机识别存放在内的数据,数据在内存中也只能是二进制的。

计算机中有符号数的表示有三种方法:原码、反码、补码。

深度剖析数据在内存中的存储_浮点数_07

注:在有符号数的二进制中,第一个二进制位为符号位,其中0代表整数,1代表负数。但对于无符号数来讲,第一位也是有效的数据位。

举个例子:int a = 10;

因为变量a中存放的是正整数,所以它的原反补码相同,为:


0 0000000000000000000000000001010      —>10的原反补码相同

因为10是个整数,而整形的类型是int,int在内存中占4个字节,又因为1个字节(byte)等于8个比特位(bit),所以10在内存中存储的是32个比特位的二进制数。

再举个例子:int b = -20;

1 0000000000000000000000000010100             —>     -20的原码
1 1111111111111111111111111101011             —>     -20的反码
1 1111111111111111111111111101100             —>     -20的补码

      对于整形来说:数据存放在内存中其实存放的是补码。

三、 大端字节序和小端字节序

1、举个例子

深度剖析数据在内存中的存储_浮点数_08

深度剖析数据在内存中的存储_数据_09

由上图可知,10的在内存中存储的二进制为:

0 0000000000000000000000000001010

-20在内存中存储的二进制为:

1 1111111111111111111111111101100

将二进制转化为16进制之后,应该是00 00 00 0a和ff ff ff ec,可是为什么内存中存放的却是反过来的呢?

这是因为,这里面存在大端存储和小端存储的概念。

2、何为大小端存储

      大端(存储)模式,是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中。

小端(存储)模式,是指数据的低位保存在内存的低地址中,而数据的高位,,保存在内存的高地址中。

深度剖析数据在内存中的存储_字节序_10

在内存中存储的是大端字节序或是小端字节序看的是编译器,编译器支持的是大端,内存中存储的就是大端字节序,支持的是小端,内存中存储的就是小端字节序。

可以理解为一个数字是10000000,把这个数字放在地址中时,大端字节序是不管三七二十一,我就从头放到尾。就变成了:

低地址   10  00  00  00   高地址(因为这个数字中,1是权重最大的,最右边的0是权重最小的。)

而小端字节序是你们给我好好的站好,“小个子的”去前面站着,“大个子的”去后面站着。就变成了:

低地址   00  00  00  10   高地址

1个16进制数等于4个2进制数。

深度剖析数据在内存中的存储_数据_11

3、如何判断当前机器的字节序

用编程实现:

方法一: 所以当a=1时,如果是小端字节序,内存中存放的应该是01 00 00 00,而大端字节序中存放的是00 00 00 01, 当*p取出第一个字节的时候,小端取出的是01,而大端取出的是00。所以当是小端时,p=1,大端时,p=0。

#include <stdio.h>
int main()
{
	int a = 1;
	char p = (char)&a;
	if (*p == 1)
	{
		printf("小端\n");
	}
	else
	{
		printf("大端\n");
	}
	return 0;
}

方法二: 所以当a=1时,如果是小端字节序,内存中存放的应该是01 00 00 00,而大端字节序中存放的是00 00 00 01, 当p取出第一个字节的时候,小端取出的是01,而大端取出的是00。所以当是小端时,ret=1,大端时,ret=0。

#include <stdio.h>
int check_sys()
{
  int a = 1;
  char p = (char*)&a;
  if (*p == 1)
  {
  	return 1;
  }
  else
  {
  	return 0;
  }
}
int main()
{
  int ret = check_sys();
  if (ret == 1)
  {
  	printf("小端\n");
  }
  else
  {
  	printf("大端\n");
  }
  return 0;
}

方法三:对方法二的优化。

#include <stdio.h>
int check_sys()
{
	int a = 1;
	char* p = (char*)&a;
	return p;//因为p的值为00或01,所以可以直接返回0或1。
}
int main()
{
	int ret = check_sys();
 	if (ret == 1)
 	{
 		printf("小端\n");
	}
	else
 	{
		printf("大端\n");
	}
	return 0;
}

四、 浮点数在内存中的存储

如:3.14、1E10,这样的数字都是浮点数。其中1E10,代表的是1.0*10^10。

1、浮点数的存储规则

国际组织IEE754规定,任何一个二进制浮点数都可以表示成如下情况:

深度剖析数据在内存中的存储_数据_12

举个例子:十进制中的13.0,二进制形式是:1101.0,写成科学计数法是:1.1010*2^3。

按照上面的规定可以得出:S=0,M=1.1010,E=3。

另一个数字是-13呢,道理一样的嘛!

二进制形式是:-1101.0 ,写成科学计数法是:-1.1010*2^3,

得出S=1,M=1.1010,E=3。

浮点数在内存中存储的方式为:

深度剖析数据在内存中的存储_数据_13

深度剖析数据在内存中的存储_字节序_14

2、浮点数中E和M的存储规则

IEE754组织对于E和M做了特殊的规定:

M表示的数字范围:1<= M<=2,以单精度浮点数举例,留给M的位置只有23个比特位,如果把前面的1也加入,留给小数的位数就只剩22位了,所以IEE754规定,M的23个比特位都留给小数,当要取出这个浮点数时,再把前面的1加上去。

对于E的存入,因为指数E是一个无符号数,但是指数E可以是负数的,如:0.001,转变为科学计数法就是1.0*10^-3。所以,IEE754规定,对于存入内存时E的真实值必须再加上一个中间数,对于8位的E,这个中间数是127;对于11位的E,这个中间数是1023。

比如13转变成科学计数法后的S为0,E值为3,M为10100000000000000000000, 但是存入内存中的E要加上127,所以存入内存中的E值为3+127=130,转变为二进制为:10000010。完整显示为:0 10000010 10100000000000000000000



3、浮点数中E的取出规则

对于E的取出,IEE754划分了三种情况:

(1) 当E中既有0又有1时

因为存入时加上了个中间数后存入的,当取出的时候要减去这个中间数得到它的真实值后,再把M前面的1补上。还是以上面的13举例:存入内存中的二进制为:

0 1000001010100000000000000000000

先把E加上的127减去,得到00000011,转变为10进制为3,再把M前面的1加上得到1.1010(此处省略19个0),以科学计数法表示得到:1.1010*2^3 —> 1101.0,转变为10进制为13.0。

(2) 当E中全为0时

IEE754规定当E为全0时,E的值直接被认定为:1-127(单精度浮点型) 或 1-1023(双精度浮点型),M前面的1也不加了,直接化为0.xxxxxx。一个数字本来就是0.xxxxx,再乘上一个2的-126次幂,结果是无限接近于0的数字。所以这样是为了表示±0的数字。

(3) 当E中全为1时

IEE754规定,当E为全1时,就算把加上的中间值减去,单精度浮点型还有128呢,1.xxxxxx的数字乘上2的128次幂是一个特别大的数字,此时代表的时±无穷大。

4、浮点数存储与取出

下面的程序输出的结果是什么?

#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;
}

输出结果:

深度剖析数据在内存中的存储_数据_15

为什么会输出下面的这些数字呢?

解析:定义了一个int型的变量n,n值为9。因为n是整形,指针类型也是int*的,如果想放在*pFloat中,需要将其强制类型转化为*float类型。

第一个printf,以整形存储,再以整形输出所以输出9。

第二个printf,以整形存储,9的二进制为:

0000 0000 0000 0000 0000 0000 0000 1001,

按照浮点数的排列为:

0 0000000000000000000000000001001

可以看到E全为0,所以就得0。

第三个printf,将*pFloat的值修改为9.0,以浮点数存储再以整形进行输出。9.0转化为2进制为:1001.0,其中S=0,E=3,M=1.001

浮点数二进制的完整显示为:0 1000001000100000000000000000000

因为以%d整形输出,所以就将0 10000011 00100000000000000000000,视为一个有符号的整数进行打印输出,

第四个printf,以浮点型存储以浮点型输出,存储的是9.0,输出的自然就是9.0。9.0在内存中存储的二进制为:

0 10000010 00100000000000000000000

先把E加上的127减去,得到00000011,转变为10进制为3,再把M前面的1加上得到1.0010(此处省略20个0),以科学计数法表示得到:1.0010*2^3 —> 1001.0,转变为10进制为9.0。

结尾:感谢你能看到这里,如果有写的不对的地方请给位不吝赐教,谢谢啦!

深度剖析数据在内存中的存储_浮点数_16