整数在内存中的存储
- 我们知道C语言有以下基本的整型类型:
char //字符型 short //短整型 int //整型 long //长整型 long long //更长的整型
- 我们可以用操作符sizeof和在<limits.h>头文件下,可以查看到各基本数据类型的所占字节的大小以及整形所能表示的范围
- 注:
- 由于char本质上是以ASCII码值存储,而ASCII是一个整数,故也将char归为整型
- short 等价于 signed short,int 等价于 signed int,long 等价于 signed long,long long 等价于 signed long long,但是C语言标准没有明确规定char、signed char,unsigned char之间的关系,但一般来说,char与signed char等价
- unsigned和signed的区别:signed的最高位为符号位。而unsigned的所有位都是数值位,所表示的数只能是非负数
- 对于二进制、八进制、十六进制及其计算还不太了解的,建议看看二进制、八进制、十六进制与十进制的相互关系
数据类型 | 占字节数 | 整型表示的最大范围 |
char | 1 | -128~127 |
unsigned char | 1 | 0~2 * CHAR_MAX + 1 |
short | 2 | -32768~32767 |
unsigned short | 2 | 0~2 * SHRT_MAX + 1 |
int | 4 | -2147483648~2147483647 |
unsigned int | 4 | 0~2 * INT_MAX + 1 |
long | 4/8 | -2147483648~2147483647 / -9223372036854775808~9223372036854775807 |
unsigned long | 4/8 | 0~2 * INT_MAX + 1 / 0~2 * LLONG_MAX + 1 |
long long | 8 | -9223372036854775808~9223372036854775807 |
unsigned long long | 8 | 0~2 * LLONG_MAX + 1 |
原码,反码,补码
- 计算机内部的二进制数都是以补码储存的。
- 正数的原码就是补码。
- 反码即将原码的所有数取反,即1变0,0变1。
- 负数原码(由十进制转二进制计算出来的数)与补码的关系:原码(保持符号位不变) -> 反码 -> 反码+1 -> 补码
- 如将负数 1001 0111 变为补码:1001 0111 -> 1110 1000 -> 1110 1001
- 举个例子:
int num_1 = 10; /* 在内存中存储的二进制序列为 0000 0000 0000 0000 0000 0000 0000 1010 */ int num_2 = -10; /* 在内存中存储的二进制序列为 原码 :1000 0000 0000 0000 0000 0000 0000 1010 反码 :1111 1111 1111 1111 1111 1111 1111 0101 补码 :1111 1111 1111 1111 1111 1111 1111 0110 */
- 那为什么计算机中存储的是反码呢?
- 使用补码可以将符号位和数值域统一处理
- 同时也可以将加法和减法做统一处理(CPU只有加法器)
整数最大值最小值的推导
- 了解了原码,反码,补码的概念后,我们就可以推导各整型的最大最小值了,这里我以char型为例:
signed char
:有符号字符型,最高位为符号位
- 由于正数的补码就是原码,最大值很容易得到,即:0111 1111 = 127
- 而对于最小值,可能有很多小伙伴会疑惑最小值为什么是-128而不是-127,这里我们来讨论一下:
- 我们可以发现char类型中,-128的原码和补码都是1000 0000,同时,这也是-0的原码,但-0和0表示的是同一个数,因此**-0就是没有意义的**,那么1000 0000这个数怎么办,为了避免浪费,我们就将它顺延到后面去,因此char的最小值,就是-127-1 = -128
- 其他整数类型也是同样的推导。
相关例题
- 注:建议先了解整型提升的相关知识,这样理解会更加深刻。
Eg1
#include<stdio.h> int main() { char num_1 = 127 + 1; /* 整型提升: 127: 0000 0000 0000 0000 0000 0000 0111 1111 1 : 0000 0000 0000 0000 0000 0000 0000 0001 和 : 0000 0000 0000 0000 0000 0000 1000 0000 由于截断 num_1:1000 0000 = -128 */ printf("num_1 = %d\n", num_1); /* num_1:1000 0000 整型提升:1111 1111 1111 1111 1111 1111 1000 0000 反码: 1111 1111 1111 1111 1111 1111 0111 1111 原码: 1000 0000 0000 0000 0000 0000 1000 0000 因此num_1 = -128 */ char num_2 = -128 - 1; /* 整型提升: -128:1111 1111 1111 1111 1111 1111 1000 0000 -1: 1111 1111 1111 1111 1111 1111 1111 1111 和:1 1111 1111 1111 1111 1111 1111 0111 1111 由于截断 num_2:0111 1111 = 127 */ printf("num_2 = %d\n", num_2); return 0; }
- 通过这一题,我们可以发现,char类型的数据其实可以构成这样的循环:
Eg2
int main() { char a[1000] = {0}; int i=0; for(i=0; i<1000; i++) { a[i] = -1-i; } printf("%d",strlen(a)); return 0; }
- 我们知道strlen碰到字符‘\0’就会停止读取,而‘\0’的ASCII值为0,因此这一题,我们就是要看当i为多少时,a[i] == 0
- 由上面的循环图,我们知道,a[i]应该依次被赋值为 -1,-2,-3……-128,127,126……0
- 因此strlen(a) = 128 + 127 = 255
大小端的介绍
- 首先我们要知道,整数在内存中有两种存储形式:大端字节序存储和小端字节序存储
- 字节序:是以字节为单位,讨论存储顺序的
- 大端字节序存储:把一个数据的低位字节的内容,从放在高地址处,一个数据的高位字节的内容,存放在低地址处
- 小端字节序存储:把一个数据的低位字节的内容,从放在低地址处,一个数据的高位字节的内容,存放在高地址处
- 例如对于如下代码
#include<stdio.h> int main() { int num = 0x11223344; return 0; }
- 我们在内存中可以看到num的存储形式:
可以看到,这是以大小端字节序存储的(左边是低地址,右边是高地址)
Tips
看完了整数在内存中的存储,不妨再了解了解浮点数在内存中的存储,修炼自己的内功,提高自己的能力。