1.数据的类型
在C语言中,基本数据类型有char、short、int、long、long long、float、double,那有没有字符串类型呢?答案是没有,哈哈哈。
在整型中,每个类型可分为有符号数和无符号数,即signed和unsigned,在vs2019中,默认的都是signed类型,所以想要表示无符号数时必须在前面加上unsigned。
除了这些类型,还有指针类型、空类型以及可以自己构造类型,如:struct、enum、union。
2.整型在内存中的存储
在了解整型在内存中的存储之前,我们先来了解一下什么时原码、反码和补码,这三种方法都可用来表示整数,由符号位和数值位组成,并且符号位的‘0’表示正数、‘1’表示负数。我们先看看负数的原反补码,
负数的原码:将该数转化成二进制形式,然后将最高位改为1;
负数的反码:在原码的基础上,符号位不变,其他位按位取反;
负数的补码:在反码的基础上+1;
而正数的原反补码就比较容易了,正数的原反补码完全一样。
对于整型来说,存在计算机中的是补码。我们来看一个例子:
分别取a、b的地址,我们可以在内存中看到a存储的是0a 00 00 00,这是以十六进制存储的,0a正好是10,由于正数的原反补码相同,所以我们无法确认其存储的是哪一个,所以我们再看b,b在内存中存储的是f6 ff ff ff,将其转换成二进制形式,正好为-10的补码,由此可以证明整型在内存中是以补码进行存放的。
细心的同学会发现,一个int类型为四个字节,为什么a的存储方式是0a 00 00 00,而不是00 00 00 0a呢?这就涉及到了另外一个知识点,大小端。
3.大小端
什么是大小端呢?
大端存储:数据的高字节存储在低地址,低字节存储在高地址。
小端存储:数据的低字节存储在低地址,高字节存储在高地址。
有了大小端的概念,我们就知道VS2019中,整数是小端存储模式。我们拿一个十六进制数来举例,0x11223344,小端存储的话,在内存中的存储方式应该是44 33 22 11,我们在编译器上来验证一下:
int a = 0x11223344;
其存储方式确实是小端。
如果要我们设计一个程序来验证当前编译器是大端还是小端,我们改怎么办呢?只要我们取一下地址就可很好的来解决了,代码如下:
int a = 1;
char* p = (char*)&a;
if (*p == 1)
printf("小端\n");
else
printf("大端\n");
有同学可能会问了,这里为什么要将&a强转成char* 呢?如果我们直接取a的地址,会得到01 00 00 00,如果只取前面的01是不是很容易判断出来其值是否与1相等呢,所以这里强转成char*,再解引用就只会得到一个字节的内容,如果为1,就是小端,为0就是大端。
4.浮点型在内存中的存储
既然浮点型没和整型一起写,那他们的存储方式自然就不同了。
国际标准规定任意一个浮点数的表示形式如下:
- (-1)^S * M * 2^E
- (-1)^s表示符号位,当s=0,V为正数;当s=1,V为负数。
- M表示有效数字,大于等于1,小于2。
- 2^E表示指数位。
所以我们在内存中存储的其实是S、M、E的值,拿5.5来举例,他应该表示成(-1)^0 * 1.011*2 ^2,也就是101.1,在小数点的左边,可以直接转换成二进制,小数点的右边则不一样,小数点右边的第一位为2的负一次方,右边第二位为2的负二次方,以此类推,我们会发现很多小数都不能精确的表达出来,如3.14。所以5.5在内存中对应的S、M、E的值分别为0、1011、2。
对于32位的浮点数其存储方式如下:
对于64位的浮点数其存储方式如下:
指数E的存储就比较复杂了,可以分为3种情况:
- 当E不全为0或不全为1时,其值等于本身+127。
- 当E为全0时,浮点数的指数E等于1-127(或者1-1023)即为真实值,
有效数字M不再加上第一位的1,而是还原为0.xxxxxx的小数。这样做是为了表示±0,以及接近于
0的很小的数字。 - 当E为全1时,如果有效数字M全为0,表示±无穷大(正负取决于符号位s)。
我们来看一个浮点数存储的例子:
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;
}
我们来看看为什么第一个*pFloat的值为0.000000,首先将n的地址强转为float *,9的表示形式为x00000009,其中s=0,E为全0,M=000 0000 0000 0000 0000 1001。所以浮点数可以写为(-1) ^ 0 × 0.00000000000000000001001×2^(-126),用十进制来打印就是0.000000;
再看num的值,9.0的表示形式为1001.0,(-1)^0 * 1.001 *2 ^2,那么内存中存储的应该是0、3 、和1001的值,写成二进制为s+E+M,其中s=0,E=3+127,M为有效数字001,要补全至23位,其表示如下:
0 10000010 001 0000 0000 0000 0000 0000,其打印出来就会是一个很大的数。