1、😃😃😃

我们已知整数的2进制表示方法有三种,即原码、反码和补码。(正整数的原反补相同,负整数原反补均不相同

原码:直接将数值按照正负数的形式翻译成⼆进制得到的就是原码。

反码:将原码的符号位不变,其他位依次按位取反就可以得到反码。

补码:反码+1就得到补码。

三种表达方式均有符号位和数值位两部分,符号位都是用0表示“正”,用1表示“负”,其数值的最高位被当作符号位,剩余的都是数值位。


对于整形来说:数据存放内存中其实存放的是补码。

原因:

1、在计算机系统中,数值⼀律⽤补码来表⽰和存储。原因在于,使⽤补码,可以将符号位和数值域统⼀处理;

2、同时,加法和减法也可以统⼀处理(CPU只有加法器)此外,补码与原码相互转换,其运算过程是 相同的,不需要额外的硬件电路。


2、大小端字节序和字节序判断

当我们了解到整数在内存中存储后,我们调试一个程序(观察其细节)

#include <stdio.h>
int main()
{
 int a = 0x11223344;
 return 0;
}

调试的时候,我们可以看到在a中的0x11223344这个数字是按字节为单位,倒着存储的。

why?🤔🤔🤔

[C语言]整数在内存中的存储_补码

2.1、什么是大小端?

其实超过⼀个字节的数据在内存中存储的时候,就有存储顺序的问题,按照不同的存储顺序,我们分为⼤端字节序存储和⼩端字节序存储,下⾯是具体的概念:

例如:123。高位时1,低位是3.

  1. ⼤端(存储)模式:是指数据的低位字节内容保存在内存的⾼地址处,⽽数据的⾼位字节内容,保存在内存的低地址处。
  2. ⼩端(存储)模式:是指数据的低位字节内容保存在内存的低地址处,⽽数据的⾼位字节内容,保存在内存的⾼地址处。

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、写代码判断机器是大端还是小端,怎么做呢?

[C语言]整数在内存中的存储_字节序_02

如上图,我们可以取出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取值范围的示意图

[C语言]整数在内存中的存储_补码_03

当值为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;
}

该题可以通过上图得知答案:即从-1开始一直减1 --> -1...-128、127、126...、1、0。strlen检测到0停下所以检测到255个数。