#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
类型的基本归类:
整形(字节):char(1) short(2) int(4) long(8)
浮点型(字节):float(4) double(8)
构造类型:数组类型arr[] 结构体类型struct 枚举类型enum 联合类型union
指针类型: int* p; char* p; float* p; void* p;
空类型: void表示空类型(无类型) 通常应用与函数的返回类型,函数的参数,指针类型。
整形在内存中的存储
计算机的整数有三种表示方法:原码,反码,补码
三种表示方法均有符号位和数值位两部分,一般二进制首位为符号位,符号位用0表示为正,用1表示为负。
正数的原码,反码,补码相同。
负数由十进制位转换为二进制位得到原码,原码符号位不变,其他位按位取反得到反码,反码+1得到补码。
所有的整形在内存中都是以补码的形式进行存储的。
为什么所有的整形在内存中都是以补码的形式进行存储的?
答:
使用补码,可以将符号位和数值位进行统一处理。同时,加法和减法也可以统一处理(CPU只有加法器,即只能算加法)。此外,补码和原码相互转换,其运算过程是相同的,不需要额外的硬件电路。
内存的存储方式从左到右是由低地址到高地址的。
大端(存储)模式:是指数据的低位保存在内存的高地址中,而数据的高位保存在低地址中。
小端(存储)模式:是指数据的低位保存在内存的低地址中,而数据的高位保存在高地址中。
由来:
在计算机系统中,是以字节为单位的,每个地址单位都对应着一个字节一个字节为8个比特位,但在C语言中除了8个bit的char外,还有16个比特位的short类型,32个比特位的int型。对于位数大于8的处理器,例如16位或32位处理器,由于寄存器的宽度大于一个字节,那么必然存在着一个如何将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式。
计算机在存储整形时,占用内存。我们常用的x86结构是小端模式,在内存中是按照16进制小端存储模式来储存整形的。
eg:20转换为16进制为00 00 00 14在内存中的存储情况为14 00 00 00 。其中4可以写成0100占四个bit,1可以写成0001也占四个bit,即14占一个字节。
-10转换为二进制为1111 1111 1111 1111 1111 1111 1111 0110,转换为16进制为ff ff ff ff ff ff ff f6,在内存中的存储情况为f6 ff ff ff ff ff ff ff。
设计一个程序来判断当前机器的字节序
void check_sys(void)
{
int a = 1;
if (*(char*)&a == 1)
printf("当前机器的字节序为小端");
else
printf("当前机器的字节序为大端");
}
int main()
{
check_sys();
return 0;
}
判断下段代码输出什么?
int main()
{
char a = -1;
signed char b = -1;
unsigned char c = -1;
printf("%d\n%d\n%d\n", a, b, c);
return 0;
}
/*输出结果为:
-1 -1 255
解析:
由于char只能存储8个bit,因此存储-1时结果为11111111,b,c由于都是char类型存储,所以也是11111111。输出时由于是%d,需要打印整形,因此系统会对存储的二进
制进行整形提升。char和signed char表示的都是有符号数,因此前面填1,则结果为11111111111111111111111111111111,最后转换成原码输出结果还是-1,而
unsigned表示无符号数,因此前面补0。即结果为00000000000000000000000011111111由于正数原码反码补码相同,因此经过计算结果为255。
*/
思考下列代码结果是多少?
int main()
{
char a = -128;
printf("%u", a);
return 0;
}
/*结果为:
4294967168
分析:
%d:打印十进制的有符号数
%u:打印十进制的无符号数
-128的原码为100000000000000000000000010000000换成补码为11111111111111111111111110000000
由于用char类型给接收,所以内存中只存储了8个bit,即10000000。用%u打印时进行整形提升后得11111111111111111111111110000000,而由于%u是打印十进制无符号
数,因此将11111111111111111111111110000000看成无符号数即正数进行打印,得到答案。
有符号位的char范围是:-128~127 signed char:10000000的值为-128(规定)
无符号位的char范围是:0~255
求下列代码的结果
#include<string.h>
int main()
{
char arr[1000];
int i;
for (i = 0; i < 1000; i++)
{
arr[i] = -1 - i;
}
printf("%d", strlen(arr));
return 0;
}
结果为:
255
解析:
对于一个char类型的数的范围为-128~127.从-1循环到-128,再进行减一操作,得到结果为127.由于strlen只有识别到0才能停止,所以循环从127再循环到0才停下来
得出的结果为128+127=255
浮点型存储数据的规则
int main()
{
int a = 9;
float* Float = (float*)&a;
printf("%d\n", a);//9
printf("%lf\n", *Float);//0.00000(1)
*Float = 9.0;
printf("%d\n", a);//1091567616(2)
printf("%lf\n", *Float);//9.00000
return 0;
}
根据下面规则分析代码结果:
(1)首先是用int类型存储的a,9的二进制为00000000000000000000000000001001,需要输出浮点型的数。依据规则,可将此二进制表示为:
0 00000000 00000000000000000001001,从此二进制可看出此时的E为全0的情况,因此此二进制根据规则可化为0.00000000000000000001001*2^-127,结果为0.
(2)9.0是用float存储的,因此可将其化为1.001*2^3。所以S=0,E=3,M=1.001.化为二进制为0 10000010 00100000000000000000000,用整形的规则打印出来则为
01000001000100000000000000000000,转化为10进制则为1091567616。
浮点型数据存储规则
对于上述代码结果可以看出,不能用浮点型的数据打印整形数据,也不能用整形数据打印浮点型数据。原因在于整形和浮点型的存储方式不同
浮点型存储数据方式:
根据IEE754规定,任意一个二进制浮点数V都可以表示成(-1)^S*M*2^E的形式.
(-1)^S表示符号位,当S=0时,V为正数,当S=1时,V为负数
M表示有效数字,2>M>=1
2^E表示指数位
eg:9.0
首先先对9.0中的正数位进行二进制转换得1001再对9.0的小数位进行二进制转换得0.得9.0的二进制为1001.0,再对其进行科学计数法的转换1.001*2^3,根据规定对照可以写出S=0,M=1.0001,E=3.
IEE954规定:
对于32位的浮点数,S为最高的1位,接着是8位的指数E,剩下的23位为有效数字M
对于64位浮点数,S仍为最高的1位,接着是11位的指数E,剩下的是52位的M
对于M和E的特殊规定:
1、由于M恒为1点几,因此可以被舍去,只保留后面的小数位,等到读取的时候再加上去。这样做的目的是可以节省一位有效数字,以32位系统为例,相当于M可以保存24位有效数字。
2、E的情况较复杂
首先,E规定为一个无符号位,这就意味着如果E为8位,它的取值范围就为0~255;如果E为64位,它的取值范围就为0~2047.但科学计数法中的E可以出现负数,因此为了使E恒为正,规定存入内存的E的真实值必须加上一个中间数,对于8位的E,这个中间数是127,对于11位的E,中间数1023.比如E为3,则存入内存的E为130,换算成二进制为01000010.
指数E从内存中取出的情况又分为三种:
E不全为0,或不全为1:
此时指数E的计算应先减去中间值127(1023),得到真实值,再将有效数字M前加上第一位的1.
比如0.5的二进制形式为0.1(即2^-1=0.5),0.5的表示形式为(-1)^0*1.0*2^-1,再将E+127=126,再将M-1,最后将表示成二进制的形式为:0 01111110 00000000000000000000000
E全为0:
此时,浮点数的指数E等于1-127(1-1023),即为真实值,有效数字M不再加上原来的第一位的1,而是还原成为0.xxxx的小数。这么做是为了表示+/-0,以接近于0的很小的数字。
E全为1:
这时有效数字M全为0,表示+/-无穷大。