1、😃😃😃
我们已知整数的2进制表示方法有三种,即原码、反码和补码。(正整数的原反补相同,负整数原反补均不相同)
原码:直接将数值按照正负数的形式翻译成⼆进制得到的就是原码。
反码:将原码的符号位不变,其他位依次按位取反就可以得到反码。
补码:反码+1就得到补码。
三种表达方式均有符号位和数值位两部分,符号位都是用0表示“正”,用1表示“负”,其数值的最高位被当作符号位,剩余的都是数值位。
对于整形来说:数据存放内存中其实存放的是补码。
原因:
1、在计算机系统中,数值⼀律⽤补码来表⽰和存储。原因在于,使⽤补码,可以将符号位和数值域统⼀处理;
2、同时,加法和减法也可以统⼀处理(CPU只有加法器)此外,补码与原码相互转换,其运算过程是 相同的,不需要额外的硬件电路。
2、大小端字节序和字节序判断
当我们了解到整数在内存中存储后,我们调试一个程序(观察其细节)
#include <stdio.h>
int main()
{
int a = 0x11223344;
return 0;
}
调试的时候,我们可以看到在a中的0x11223344这个数字是按字节为单位,倒着存储的。
why?🤔🤔🤔
2.1、什么是大小端?
其实超过⼀个字节的数据在内存中存储的时候,就有存储顺序的问题,按照不同的存储顺序,我们分为⼤端字节序存储和⼩端字节序存储,下⾯是具体的概念:
例如:123。高位时1,低位是3.
- ⼤端(存储)模式:是指数据的低位字节内容保存在内存的⾼地址处,⽽数据的⾼位字节内容,保存在内存的低地址处。
- ⼩端(存储)模式:是指数据的低位字节内容保存在内存的低地址处,⽽数据的⾼位字节内容,保存在内存的⾼地址处。
2.2、为什么有大小端?
因为在计算机系统中,我们以字节为单位的,每个地址单元都对应着一个字节,一个字节为8个比特位。而在C语言中除了8bit的char外,还有16bit的short型,32bit的long型(具体依赖编译器)。对于位数大于8位的处理器(16/32位),由于寄存器宽度⼤于⼀个字节,那么必然存在着⼀个如何将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式。
例如:⼀个 16bit 的 short 型 x ,在内存中的地址为 0x0010 , x 的值为 0x1122 ,那么 0x11 为⾼字节, 0x22 为低字节。对于⼤端模式,就将 0x11 放在低地址中,即 0x0010 中, 0x22 放在⾼地址中,即 0x0011 中。⼩端模式,刚好相反。我们常⽤的 X86 结构是⼩端模式,⽽ KEIL C51 则为⼤端模式。很多的ARM,DSP都为⼩端模式。有些ARM处理器还可以由硬件来选择是 ⼤端模式还是⼩端模式。
2.3、练习
2.3.1、写代码判断机器是大端还是小端,怎么做呢?
如上图,我们可以取出a的第一个字节,若第一个字节为1,则为小端。若第一个字节为0,则为大端。
如何取出第一个字节呢?--->解引用被强制类型转换为char*的地址即*(char*)&a。
//设计一个小程序来判断当前机器的字节序
#include<stdio.h>
int check_sys()
{
int a = 1;
return (*(char*)&a);//小端返回1,大端返回0
}
int main()
if (check_sys() == 1)
{
printf("小端\n");
}
else
{
printf("大端\n");
}
return 0;
}
2.3.2、练习2
#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
解析:截断--->整型提升--->原反补码。如下图:
#include <stdio.h>
int main()
{
char a = -1;
//10000000000000000000000000000001
//11111111111111111111111111111110
//11111111111111111111111111111111
//截断
//11111111 - a
//11111111111111111111111111111111 - 补码
//10000000000000000000000000000000
//10000000000000000000000000000001-> -1
signed char b = -1;
//10000000000000000000000000000001
//11111111111111111111111111111110
//11111111111111111111111111111111
//11111111 - b
//11111111111111111111111111111111
//10000000000000000000000000000000
//10000000000000000000000000000001-> -1
unsigned char c = -1;
//10000000000000000000000000000001
//11111111111111111111111111111110
//11111111111111111111111111111111
//11111111 - c
//00000000000000000000000011111111
//00000000000000000000000011111111 - 255
printf("a=%d,b=%d,c=%d", a, b, c);
//%d - 以十进制的形式打印有符号的整型
//-1
/ return 0;
//}
2.3.3、练习3
#include <stdio.h>
int main()
{
char a = -128;
printf("%u\n",a);
return 0;
}
解析:最后输出结果为4294967168
#include <stdio.h>
int main()
{
char a = -128;
//10000000000000000000000010000000
//11111111111111111111111101111111
//11111111111111111111111110000000
//10000000 - a
//11111111111111111111111110000000
printf("%d\n", a);//%d 打印有符号整数
//由于无符号此时的补码就是原码,则打印结果为4294967168
//10000000000000000000000001111111
//10000000000000000000000010000000
//-128
printf("%u\n", a);//%u 打印无符号整数
//
return 0;
//}
当a=128时,同理会发生截断,整型提升。最后的结果仍为4294967168
char取值范围的示意图
当值为127时,再加1,则会直接变成-128
2.3.4、练习4
#include <stdio.h>
int main()
{
char a[1000];
int i;
for(i=0; i<1000; i++)
{
a[i] = -1-i;
}
printf("%d",strlen(a));
return 0;
}