一、 数据的类型
1、C语言中的数据类型
可以分为以下几种:
2、在C语言中
char类型占1个字节
short类型占2个字节
int类型占4个字节
long类型没有固定具体的大小,所占空间>=int类型所占空间
long long类型占8个字节
float类型占4个字节
double类型占8个字节……
3、类型的意义
(1)这个(种)类型开辟空间的大小,开辟空间的大小决定了定义的数据的存储的范围及使用的范围。
(2)看待开辟内存空间的视角不同。
4、数据的类型
可以划分为五种,分别为:整形类型、浮点类型、指针类型、构造类型、空类型。
整形类型:
char类型虽然是字符类型,但在存储char定义的变量时,存储的是变量的ASCII码值,ASCII码值是整数,所以char类型属于整形变量的范畴。
char类型没有具体的规定它是signed char 或 unsigned char,它的类型是什么,是取决于编译器的,不同的编译器对char的类型有着不同的规定。
浮点类型:
构造类型:
大家可能对数组也是构造类型感觉到奇怪,我们定义一个数组,int arr[10] = {0}; 其中,arr是数组的变量名,而int [10]是这个数组的类型,如果将[]内的值从10修改为5,那这个数组的类型就变成了int [5],数组类型发生了改变,所以说数组也是一种构造类型。
指针类型:
空类型:
二、 整数在内存中的存储
任何一个变量存储在内存中都需要开辟一块空间来存放,又因为计算机只认识机器语言,机器语言就是二进制的0和1组成的语言,所以想让计算机识别存放在内的数据,数据在内存中也只能是二进制的。
计算机中有符号数的表示有三种方法:原码、反码、补码。
注:在有符号数的二进制中,第一个二进制位为符号位,其中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、举个例子
由上图可知,10的在内存中存储的二进制为:
0 0000000000000000000000000001010
-20在内存中存储的二进制为:
1 1111111111111111111111111101100
将二进制转化为16进制之后,应该是00 00 00 0a和ff ff ff ec,可是为什么内存中存放的却是反过来的呢?
这是因为,这里面存在大端存储和小端存储的概念。
2、何为大小端存储
大端(存储)模式,是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中。
小端(存储)模式,是指数据的低位保存在内存的低地址中,而数据的高位,,保存在内存的高地址中。
在内存中存储的是大端字节序或是小端字节序看的是编译器,编译器支持的是大端,内存中存储的就是大端字节序,支持的是小端,内存中存储的就是小端字节序。
可以理解为一个数字是10000000,把这个数字放在地址中时,大端字节序是不管三七二十一,我就从头放到尾。就变成了:
低地址 10 00 00 00 高地址(因为这个数字中,1是权重最大的,最右边的0是权重最小的。)
而小端字节序是你们给我好好的站好,“小个子的”去前面站着,“大个子的”去后面站着。就变成了:
低地址 00 00 00 10 高地址
1个16进制数等于4个2进制数。
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规定,任何一个二进制浮点数都可以表示成如下情况:
举个例子:十进制中的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。
浮点数在内存中存储的方式为:
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;
}
输出结果:
为什么会输出下面的这些数字呢?
解析:定义了一个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。
结尾:感谢你能看到这里,如果有写的不对的地方请给位不吝赐教,谢谢啦!