1.整型
空类型:
void表示空类型
void无法定义变量,因为类型不明确,就无法定义变量,开辟空间大小。但可以做函数的返回值,前提是该函数的返回类型为空。
void *
void *
可以定义变量,能赋值,说明void *
本身给变量开辟了空间,但void *
对应的变量不能被直接解引用。
void *
有什么用呢?
void *
有非常特殊的应用,就是void *
可以用来接收任意类型,常用于接受任意指针。
整型在内存中的存储:
- 整型在计算机中存储的是补码。
无符号数:原码=反码=补码
有符号数:最高位代表符号位,正(0),负(1)
有符号正数:原码=反码=补码
有符号负数:进行原反补转换
原码:对应数的绝对值的二进制序列,特殊:有符号数,其中最高位要设置为符号位
反码:符号位不变,其它位按位取反
补码:反码+1(符号位要参与运算,即使通常很少影响到符号位)
2.数据写入的过程
原反补约束的是原始数据
先给变量开辟空间
将数据转换位对应的二进制序列,但转化的过程和目标变量无关
数据写入到对应的开辟好的空间中
完成
写入数据:本质是现将数据转化成对应的二进制序列(与目标变量无关),然后写入对应的内存空间里
- 数据读取的过程
读一个变量的时候,先看它的数据类型,决定了我们如何看待该变量内部的二进制序列的含义
不管我们如何看待二进制序列,二进制序列本身不会发生改变,但是经过类型的解释,二进制代表的含义就会发生改变
读入数据:本质是根据变量的类型,来解释内存里面的二进制是什么含义
- 变量的类型起的作用
开辟空间的大小
读取数据的时候,如何解释二进制序列
- 大小端存储模式
大端模式:数据的低位存储在高地址中,数据的高位存在低地址中
小端模式:数据的低位存储在低地址中,数据的高位存在高地址中
数据按照字节为单位存储在计算机中,可以被划分为若干部分,每一部分对应的“权值”是不同的,只要有不同就会有高低。
内存访问的基本单位是字节,只要是字节,就会有地址,只要有地址,就会有大小。
在数据读取时,同样也要考虑大小端问题。
如何知道当前计算机是否为大端存储还是小端存储?
可以写一个代码实现
int main()
{
int i = 1;
char *p =(char *)&i;
printf("%d\n", *p);
return 0;
}
如果是大端,输出0,如果是小端输出1。
几个毕竟有意思的题:
①
int main()
{
char a[1000];
int i;
for (i = 0; i < 1000; i++)
{
a[i] = -1 - i;
}
printf("%d\n", strlen(a));
return 0;
}
strlen函数,遇到‘\0’就停下来,本质上就是0,计算长度的时候,不会包含0。
char类型的取值范围为[-128,127]。
a[i]的由-1依次减小,直到-128,下一个值为127。
即a[128]=-129
但是char类型被解释为127
//1000 0001 129
//1 0111 1111 -129
但char类型只有一个字节大小,会发生截断问题,则为
//0111 1111 127
利用这个图片来理解这个题,逆时针走,直到0,然后再继续走,但求strlen时,在0处就会停下,故中间经历了255个元素,故该题的答案最后为255。
②
unsigned char i = 0;
int main()
{
for (i = 0; i <= 255; i++)
{
printf("bit!");
}
return 0;
}
变量i的数据类型为unsigned char 故i的取值范围为[0,255],所以main函数里的循环条件会一直满足,导致死循环。
总结:看一个变量时,必须先看该变量的数据类型,因为数据类型决定了我们如何看待变量,以及变量的取值范围和大小(即如何解释这个变量)。
2.浮点型
浮点型的存储和整型的区别比较大,先看一个例子。
int main()
{
int n = 9;
float *p = (float *)&n;
printf("%d\n", n);
printf("%f\n", *p);
*p = 9.0;
printf("%d\n", n);
printf("%f\n", *p);
return 0;
}
输出结果是什么呢?
为什么差距有这么大呢?
先看看浮点型数据在内存中如何存储:
根据IEEE754标准,任意一个二进制浮点数可以表示为下面的形式:
(-1)^S * M * 2^E
(-1)^S表示符号位,当s=0时,为正数,s=1时,为负数
M表示有效数字,大于等于1,小于2
2^E表示指数位
比如说9.0,写成二进制为1001.0,相当于1.001 * 2^3。按照上面的格式,则S=0,M=1.001,E=3
单精度浮点数存储模型为:
IEEE754标准规定,M大于等于1,小于2,M可以写成1.xxxxxxx,但在计算机保存M的时候,只保存后面的xxxxxx,最后读取的时候再将第一位的1加上去。这样做的目的是可以节省1位有效数字,虽然M只有23位,但可以保存24位有效数字。
比如说1.01,保存的时候就为01。
指数E的介绍:
- E为一个无符号整数(unsigned int),如果E为8位,则取值范围为[0,255],E为11位(双精度浮点数),取值范围为[0,2047]。在科学计数法中,是存在负数的,所以IEEE754规定,在存入内存时E的真实值必须再加上一个中间数,对于8位的E,中间数为127,对于11位的E,中间数为1023。比如2^10的E是10,所以保存32位浮点数时,保存为10+127=137.即10001001.
- E不全为0或不全为1时,直接用指数E的计算值减去127,得到真实值,再将有效数字M前加上第一位的1。例如9.0
0 1000 0010 0010 0000 0000 0000 0000 000
写代码验证一下:
int main()
{
float n = 9;
int *a = (int*)&n;
int i = 31;
for (; i >= 0; i--)
{
printf("%d ", ((*a) >> i) & 1);
}
printf("\n");
return 0;
}
符合预期结果。
- E全为0,此时,浮点数的指数E等于1-127(1-1023),即为真实值,有效数字不再加上第一位的1,而是还原为0.xxxxx的小数,,这是为了表示±0,以及接近0的很小的数字。
- E全为1,如果有效数字M全为0,则表示±无穷大。
像上面的9.0,如果直接给整型 n,其二进制会被解释为一个特别大的数。