整数在内存中的存储
整数的二进制表示形式有三种,原码、反码、补码。而整数在内存中的存储形式为补码。
有符号整数
对于有符号整数,三种表示形式都由符号位和数值位两部分组成。最高的一位被当作符号位,正数符号位为“1”,负数符号位为“0”。
有符号整数的原码是将数值位以二进制形式表示,第一位为符号位。反码是符号位不变,数值位各按位取反。补码是反码加1。
无符号整数
对于无符号整数,每一位都是数值位。
其原码反码补码都相同,将整数直接由二进制形式表示即可。
补码的好处
补码的出现使得二进制数的加法与减法可以使用同一个运算器同一个电路进行,无需再为减法配置一个运算器。而且,从原码到补码的运算(符号位不变,数值位按位取反加1)与补码到原码的运算一致,也是符号位不变,数值位按位取反加1,读者可自行验证。
大小端字节序和字节序的判断
什么是大小端字节序
对于整型a=0x11223344,他在内存中占四个字节,这四个字节是怎么排序的呢,存放11的字节和存放22,33,44的字节的位置关系是什么样的呢?
我们进行调试,查找a的地址,可以看到,存放44的字节放在这块内存的低地址位置,存放11的字节放在高地址位置。而对于一个十六进制数,44代表4+4*16,11代表4*(16^6)+4*(16^7),显然,11的位数高于44,我们把4的位数称为较低位,把1的位数称为较高位。就如十进制数17,1的位数比7高。而如下图所示,a的最低位4存放到内存的较低地址,被称为小端存储。与之对应的是大端存储,低位存在内存的高地址处。大端存储和小端存储方式由编译器决定,在vs编译器中,由下图可知使用的是小端存储,其他编译器可能是大端存储。
如何确定字节序类型
对于a=0x11223344,我们可以发现,在小端字节序存储下,他的第一个字节存放的是0x11,那么在大端字节序存储下,就应该存放的是0x44。也就是说,我们可以通过第一个字节的值来确定字节序类型。取出a的第一个字节,对其进行解引用即可。由于a为整型,需要先将&a强制类型转换为char*类型才能访问一个字节的地址。
#include<stdio.h>
int main()
{
int a = 0x11223344;
int b = *(char*)(&a);
if (b == 0x11)
printf("大端");
else
printf("小端");
return 0;
}
练习
一、
此题给出不同类型的a,b都赋值为-1,再将其以整型的形式打印,求打印结果。
#include<stdio.h>
int main()
{
char a = -1;
unsigned char b= -1;
printf("a=%d,b=%d",a,b);
return 0;
}
-1是一个有符号整型,它在内存空间中以补码形式存储。
-1的原码为10000000000000000000000000000001
反码为 1111111111111111111111111111110
补码为 1111111111111111111111111111111
而要想将其放在a和b中,因为a是char类型,b为unsigned char类型,都只有一个字节长度,所以只能放进后八个二进制位,
即a=11111111,b=11111111。
打印时因为要将ab以整型形式打印,需要进行整型提升。
a是有符号数,有符号数进行整型提升时补符号位,提升后补码为11111111 11111111 11111111 11111111
b是无符号数,整型提升时补0,提升后补码为
00000000 00000000 00000000 11111111
对a,b求原码,有符号数原码为补码除符号位按位取反再加1,无符号数原码补码相等。
a的原码为10000000 00000000 00000000 00000001
b的原码为 00000000 00000000 00000000 11111111
则a为-1,b为2^8-1=255
二、
此题给出一个char类型的-128,要求以无符号整型形式打印,求打印结果。
#include<stdio.h>
int main()
{
char a = -128;
printf("%u", a);
return 0;
}
-128的原码为10000000 00000000 00000000 10000000
补码为11111111 11111111 11111111 10000000
则a中为11111110
因为a是char类型,整型提升时补1,提升后补码为
11111111 11111111 11111111 10000000
以unsigned int 形式打印时,最高位为数值位,则打印结果应该是一个很大的正数。