所谓整形数据,包括但不限于int类型,还包括有符号或无符号的long,longlong,甚至char。
为啥还有char呢?因为char类型在内存中存储的是字符对应的ASCII码值,而ASCII码是一个整数,所以把char归结到整形家族中。
那么整形数据在内存中是如何存储的呢?
首先有一个常识,计算机中都是以二进制形式存储的,而一个二进制位(1/0)占用的空间是一个比特(bit)。举个例子,一个int类型是4个字节,相当于32bit,所以在计算机中存储的是32个(1/0)的二进制位。
整形家族的数据的二进制表示有三种形式,分别是原码,反码,补码。而内存中存储的是补码。对于有符号的类型,正数的原码,反码,补码相同,都是直接把这个数据转换成对应的二进制;而负数的原码就是直接转换成对应的二进制,补码和反码需要计算。而对于无符号的类型就当成有符号类型的正数来处理(因为无符号类型是没有负数的)。
那么问题来了,什么是有符号,什么是无符号呢?这要看类型前有没有加上signed和unsigned两个关键字。比如说signed char,signed int,signed long (int),signed long long (int)表示的都是有符号类型,如果把signed都换成unsigned,表示的就是无符号类型。那么如果没有这两个关键字呢?int,long (int),long long (int)在C语言语法规定中表示的都是有符号类型,所以写int和写signed int是同一个意思,其他几个类型以此类推。而对于char类型,C语言中并没有与法规定具体是有符号的类型还是无符号的类型,具体是有符号还是无符号取决于编译器的实现,比方说visual studio中char类型是有符号的。
前面说了,有符号类型的原码,反码,补码需要计算,那么具体是如何计算的呢?
举个例子:对于int类型的-1这个数,由于是负数,所以最高位(符号位)是1,直接转换成二进制就是对应的原码
-1的原码:10000000000000000000000000000001(总共32个比特位)
把原码符号位不变,其他位按位取反得到的就是反码
-1的反码:11111111111111111111111111111110
反码+1得到的就是补码
-1的补码:11111111111111111111111111111111
如果是其他负数,计算的方式也是一样的,原码就是直接转换成二进制,反码就是原码的符号位不变,其他位按位取反(1变成0,0变成1)得到的,反码加1就能得到补码,在内存中存储的就是补码,比如int类型的-1在内存中存储的就是32个1。
再比如,int类型的-10是怎么存储的呢?
-10的原码:100000000000000000000000000001010
-10的反码:111111111111111111111111111110101
-10的补码:111111111111111111111111111110110
接下来再讲一个知识:4个二进制位可以转换成一个十六进制位,所以把-10的补码转换成十六进制位得到的就是:
0xfffffff6
其中两个16进制位就相当于8个二进制位,也就是一个字节,而内存中是以字节为单位划分成一个个很小的内存单元来管理的,每个内存单元有一个唯一对应的指针。
我们通过调试来看看int类型的-10在内存中是怎么存储的(用VS2022):
可以发现,随着地址从小到大变化,数据是倒着存的。事实上,如果存储的数据大于1个字节,就必然涉及到存储的顺序的问题。有两种存储顺序,通俗来说就是正着存或者倒着存,在VS2022里采用的是倒着存。
大端字节序:数据的高位字节处的数据放在低地址处,低位字节处的数据放在高地址处,也就是直观上的正着存。
小端字节序: 数据的高位字节处的数据放在高地址处,低位字节处的数据放在低地址处,也就是直观上的倒着存。
在VS2022里采取的是小端字节序。
我们可以写一个程序来判断系统的大小端。方法很简单,把一个int类型的1存在内存里,然后用char*的指针来访问第一个字节,如果是大端字节序,那么存储的就是00 00 00 01,第一个字节是0,小端字节序同理,第一个字节是1。



