数据的基本类型
一 整形数据
char | 字符数据类型,在内存中占一个字节 |
short | 短整型,在内存中占两个字节 |
int | 整形,在16位的平台中占两个字节,在32位平台中占四个字节 |
long | 长整型,在32位平台中占四个字节,在64位平台中占八个字节 |
long long | 更长的整型,占八个字节 |
float | 单精度浮点型,占四个字节 |
double | 双精度浮点型,占八个字节 |
一个变量的创建是需要在内存中开辟空间的,而空间的大小是根据不同的类型来决定的。数据在这些空间中以二进制补码的形式存储,而计算机中有符号数有原码,反码,补码三种表示形式,并且还有符号位和数值位,符号位用0表示正数,1表示负数
数值位正数和负数的表示方式则不同。
//表示数字20
原码: 0000 0000 0000 0000 0000 0000 0001 0100
16进制:0x 0 0 0 0 0 0 1 4
0x00 00 00 14
//表示数字-10
原码: 0000 0000 0000 0000 0000 0000 0000 1010
反码: 1111 1111 1111 1111 1111 1111 1111 0101
补码: 1111 1111 1111 1111 1111 1111 1111 0110
16进制:0x f f f f f f f 6
0xff ff ff f6
-10在内存中存储的二进制数就是1111 1111 1111 1111 1111 1111 1111 0110
如果一个数是正数,那么在内存中,他的原码,反码和补码是一样的,但是如果这个数是负数,他会以补码的形式进行存储。
int main()
{
int a = 20;
int b = -10;
int* p = &a;
int* q = &b;
return 0;
}
在这组数据中,会发现数据的存储顺序是倒着的。
这是因为大端和小端这两种存储模式
大端:是数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中。低地址–>>高数据 例如:手机
小端:是指数据的地位保存在内存的低地址中,数据的高位则保存在内存的高地址中。低地址–>>低数据 例如:电脑
判断计算机是大端还是小端:
union uu//联合结构体
{
int i;
char a;
};//特点:i和a共用一块内存
int CheakSys()
{
union uu un;
un.i = 20;
return un.a;
}
int main()
{
int ret = CheakSys();
if (ret == 0x14)
{
printf("小端\n");
}
else
{
printf("大端\n");
}
return 0;
}
对二进制的新理解
#include<stdio.h>
#include<Windows.h>
int main()
{
unsigned int i;//i是无符号类型
for (i = 9; i >= 0; i--)
{
Sleep(100);
printf("%u\n", i);
}
return 0;
}
#include<stdio.h>
int main()
{
unsigned char i = 0;//i是无符号char类型
for (i = 0; i < 256; i++)
{
printf("hello word\n");
}
return 0;
}
经过运行可以看到,两个程序都陷入了死循环,这是因为二进制数据存放的原因,它们可以近似的看成一个环形,以有符号数来说,char类型的范围为-128 ~ 127,就形成了从0-127- -128- -1 -0这样的一个环,所以,i永远不会等于128,就会造成死循环,类似于钟表的一个结构
127 0111 1111
1 0000 0001
127+1 1000 0000 (-128的原码)
反码 1111 1111
补码 1 0000 0000
-1 原码 1000 0001
反码 1111 1110
补码 1111 1111
1 原码 0000 0001
-1+1 1 0000 0000 (0的原码)
数据的整形截断和整形提升问题
不同的数据类型所表示的数据存储范围都有差异,当给一个变量输入一个超出改类型最大存储范围的数时,再对这个变量进行打印,结果则会出现差异,就像下面这两个例子一样:
int main()
{
char a = -128;
printf("%u\n",a);
return 0;
}
int main()
{
char a = 128;
printf("%u\n",a);
return 0;
}
-128 在内存中的存储是
10000000 00000000 00000000 10000000-->这是他的原码
11111111 11111111 11111111 01111111-->这是他的反码
11111111 11111111 11111111 10000000-->这是他的补码
char 类型只占一个子节大小,所以在给a初始化赋值后,就只能把-128后8位赋值给a,因为电脑是小端,低地址存放低数据,所以a的补码是 1000 0000 ,这里就发生了整形的截断,下面打印a 的时候,%u 是以32位无符号数打印,就会在a的高位上补1(关于补位,如果是无符号数,就补0;如果是有符号数,就补他的符号位的数字),所以最终打印的
a
11111111 11111111 11111111 10000000 --->4294967168
这就发生了整形的提升。
二 浮点型在内存中的存储
int main()
{
int n = 9;
//0000 0000 0000 0000 0000 0000 0000 1001
float *pFloat = (float *)&n;
printf("n的值为:%d\n",n);//9
printf("*pFloat的值为:%f\n",*pFloat);//0.000000
//以浮点类型视角去读数据
//0 00000000 00000000000000000001001
*pFloat = 9.0;
//1001.0->1.001*2^3
//0 10000010 00100000000000000000000
printf("num的值为:%d\n",n)
//1091567616 整形的视角去读取数据
printf("*pFloat的值为:%f\n",*pFloat);//9.0
return 0;
}
出现这种情况是因为,国际标准IEEE(电气和电子工程)754,规定任意一个浮点数V可以写成下面的形式
(-1)^S*M*2^E
(-1)^S表示符号位,当S=0时,V为正数;当S=1时,V为负数。
M表示有效数字,大于等于1,小于2。
2^E表示指数位。
规定:对于32位的浮点数,最高一位是符号位S,下来是8位指数E,最后是23位有效数字M(注意:因为有效数字本身就是小数位的,所以在出现位数不够的情况下,应该在后面补零 相当于0.11100000000,如果在前面补零,就会成为0.0000011111,使得原来的数变小)
十进制:5.0
二进制:101.0 相当于1.01*2^2
(-1)^S*M*2^E
S=0 M=1.01 E=2
而在实际中,对于指数E位,如果E是八位,它的取值范围是0~255,如果E是11位,它的取值范围是0~2047,为了防止在计数过程中出现负数的情况,IEEE 754规定:存入内存时,E的真实值必须加一个中间数,对于8位的E,中间数是127,对于11位的E,中间数是1023.
2^10
E=10
保存成32位浮点数的时候,保存的数据是 10+127=137 -->1000 1001
E全为0 ----> 表示无限接近于0 浮点数里没有绝对的0
E全为1 ----> 表示正负无穷大