目录:
1.整数在内存中的存储
2.⼤⼩端字节序和字节序判断
正文:
1.整数在内存中的存储
整数的2进制表⽰⽅法有三种,即原码、反码和补码 三种表⽰⽅法均有符号位和数值位两部分,符号位都是⽤0表⽰“正”,⽤1表⽰“负”,⽽数值位最 ⾼位的⼀位是被当做符号位,剩余的都是数值位。
正整数的原、反、补码都相同。
负整数的三种表⽰⽅法各不相同。
原码:直接将数值按照正负数的形式翻译成⼆进制得到的就是原码。
反码:将原码的符号位不变,其他位依次按位取反就可以得到反码。
补码:反码+1就得到补码。
对于整形来说:数据存放内存中其实存放的是补码。 (原因:在计算机系统中,数值⼀律⽤补码来表⽰和存储。 原因在于,使⽤补码,可以将符号位和数值域统⼀处理; 同时,加法和减法也可以统⼀处理(CPU只有加法器)此外,补码与原码相互转换,其运算过程是 相同的,不需要额外的硬件电路。 )
2.⼤⼩端字节序和字节序判断
#include <stdio.h>
int main()
{
int a = 0x11223344;
return 0;
}
在这段代码中,我们调试可发现:
我们第一感觉可能是感觉有点别扭,它为什么要倒着放? 但其实也有正着放的,而倒着放的叫做小端,正着放的叫大端,接下来就是要了解什么是大端和小端:
⼤端(存储)模式:是指数据的低位字节内容保存在内存的⾼地址处,⽽数据的⾼位字节内容,保存在内存的低地址处。
⼩端(存储)模式:是指数据的低位字节内容保存在内存的低地址处,⽽数据的⾼位字节内容,保存在内存的⾼地址处。
那么如果让你写一个代码来判断编译器是哪种储存模式,又该怎么写呢?
代码:
详解:
创建一个整形a,给其赋值为1,如果是大端储存,a在内存中就是以00 00 00 01,这里用16进制数字表示,看起来更加方便,而小端储存就是01 00 00 00。再创建一个char类型的变量b,使b = a;这样a的第一个字节的数字就会被赋值到b中,然后打印b,如果是大端,b的结果就是0,如果是小端b就是1。
练习1:
#include <stdio.h>
int main()
{
char a = -1;
signed char b = -1;
unsigned char c = -1;
printf("a=%d,b=%d,c=%d", a, b, c);
return 0;
}
答案:a=-1,b=-1,c=255
详解:-1的原码:1000 0000 0000 0000 0000 0000 0000 0001
反码:1111 1111 1111 1111 1111 1111 1111 1110
补码:1111 1111 1111 1111 1111 1111 1111 1111
把-1赋给a,a的类型是char,char的大小是1个字节 = 8个比特位,为1111 1111,(char是signed char,还是unsigned char取决于编译器,在vs中是signed char), 1111 1111整形提升后为1111 1111 1111 1111 1111 1111 1111 1111,而1111 1111 1111 1111 1111 1111 1111 1111的原码为1000 0000 0000 0000 0000 0000 0000 0001,,十进制为-1
b与a同理,也为-1;
而c是unsigned char,所以编译器会将1111 1111 1111 1111 1111 1111 1111 1111视为正数,而正数的原码,反码和补码相同,转换位十进制数字为255。
练习2:
#include <stdio.h>
int main()
{ char a = -128;
printf("%u\n",a);
return 0;
}
答案:4294967168
详解:-128的补码:1000 0000 0000 0000 0000 0000 1000 0000
a是char类型变量,占8个比特位,1000 0000,整形提升后为1111 1111 1111 1111 1111 1111 1000 0000,%u是无符号整形的占位符,所以将1111 1111 1111 1111 1111 1111 1000 0000视为正数,十进制为4294967168。
练习3:
#include <stdio.h>
#include<string.h>
int main()
{ char a[1000];
int i;
for(i=0; i<1000; i++)
{
a[i] = -1 - i;
}
printf("%d", strlen(a));
return 0;
}
答案:255
详解:
我们可以换个角度理解,如图:
a[0] = -1 ,a[1] = -2, a[2] = -3……......-128 127.............. 0 -1 -2........
strlen函数遇到'\0'停止,而'\0'的ASCLL码值==0,所以会在第一个0处停止,由上可知strlen(a)==255。
练习4:
#include <stdio.h>
int main()
{ unsigned int i;
for(i = 9; i >= 0; i--)
{ printf("%u\n",i); }
return 0;
}
此代码是无限循环,问题在于 i
被定义为 unsigned int
类型,而循环条件 i >= 0
使用了无符号整数。在 C 语言中,无符号整数永远不会小于零,因此当 i
递减到 0 后,下一次循环 i
将变为一个非常大的正数,而不是 -1。
练习5:
//在小端储存模式中
#include <stdio.h>
int main()
{ int a[4] = { 1, 2, 3, 4 };
int *ptr1 = (int *)(&a + 1);
int *ptr2 = (int *)((int)a + 1);
printf("%x,%x", ptr1[-1], *ptr2);
return 0;
}
答案:4,200000
详解:在我们的深入理解指针的博客中提到过,&a是取出整个数组的地址,所以:
而ptr1[-1]等价于*(ptr1 - 1),ptr1是int*类型的指针,-1向后移动四个字节,指向4的地址;
而在(int *)((int)a + 1)中,,a被强制转换为了int类型,假设a数组的首元素地址是0x01ff1120,加一之后就变成了0x01ff1121,如图:
打印*ptr2,从图上的位置开始往后四个字节,也就是0x02 00 00 00,打印出来就是200000。