整形数据在内存中的存储
- 1.整形家族
- 2.(原码、反码、补码)基础知识
- 3.大小端
- 3.1 什么是大小端
- 3.2 为什么有大端和小端
- 3.3 一道关于大小端字节序的面试题
- 3.4 关于整形数据存储的题目(7题)
- 3.4.1
- 3.4.2
- 3.4.3
- 3.4.4
- 3.4.5
- 3.4.6
- 3.4.7
- 4.总结
1.整形家族
signed可省可不省,一般省略不写unsigned charsigned charunsigned short [int]signed short [int]unsigned intsigned intunsigned long [int]signed long [int]
2.(原码、反码、补码)基础知识
计算机中的整数有三种2进制表示方法,即原码、反码和补码。
三种表示方法均有符号位和数值位两部分,符号位都是用0表示“正”,用1表示“负”。
正数的原、反、补码都相同。
负整数的三种表示方法各不相同。
- 原码
直接将数值按照正负数的形式翻译成二进制就可以得到原码。 - 反码
将原码的符号位不变,其他位依次按位取反就可以得到反码。 - 补码
反码+1就得到补码。
对于整形来说:数据存放内存中其实存放的是补码。
原因在于:在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统一处理;
同时,加法和减法也可以统一处理(CPU只有加法器)此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。
int main()
{//为了方便表示,在vs中数据以十六进制呈现出来//0x0000000a ->10int num = 10;//0xfffffff6 ->-10int num2 = -10;
}
我们可以看到对于num和num2分别存储的是补码。但是我们发现顺序有点不对劲。
这是又为什么?解下来介绍大小端
3.大小端
3.1 什么是大小端
大端(存储)模式:把一个数据的低位字节的内容,存放在高地址处,把一个数据的高位字节的内容,存放在低地址处。
大端(存储)模式:把一个数据的低位字节的内容,存放在低地址处,把一个数据的高位字节的内容,存放在高地址处。
3.2 为什么有大端和小端
因为在计算机系统中,是以字节为单位的,每个地址单元都对应着一个字节,一个字节为8 bit。但是在C语言中除了8 bit的char之外,还有16 bit的short型,32 bit的long型(要看具体的编译器),另外,对于位数大于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如何将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式。
常用的 X86 结构是小端模式,而 KEIL C51 则为大端模式。很多的ARM,DSP都为小端模式。有些ARM处理器还可以由硬件来选择是大端模式还是小端模式。
3.3 一道关于大小端字节序的面试题
设计一个小程序来判断当前机器的字节序。
#include<stdio.h>
int check_sys1()
{int a = 1;return (*(char*)&a);
}
//方法2int check_sys2()
{union{int i;char c;}un;un.i = 1;return un.c;
}
int main()
{int ret = check_sys1();if (ret == 1)printf("小端字节序存储\n");elseprintf("大端字节序存储\n");printf("%d", ret);
}
3.4 关于整形数据存储的题目(7题)
原码 = 补码-1,然后取反
补充知识:原码 = 补码取反+1
3.4.1
int main()
{char a = -1; //char是有符号字符类型,并且取值范围能存下-1,因此打印-1//10000000 00000000 00000000 00000001 原码//11111111 11111111 11111111 11111110 反码//11111111 11111111 11111111 11111111 补码//11111111 -a 截断(因为char只有1字节=8bit)//%d整形打印: 先提升,然后将补码转换成原码//11111111 11111111 11111111 11111111 补码//10000000 00000000 00000000 00000000 反码//10000000 00000000 00000000 00000001 原码 ->-1signed char b = -1; //char 和 signed char是一样的,知识char默认为有符号,所以省略了signed,结果也是-1//b同a一样unsigned char c = -1; //此时char是无符号//00000000 00000000 00000000 11111111 补码//11111111 -c 截断,因为是无符号,所以整形提升时前面补0//%d打印:需要发生整形提升//00000000 00000000 00000000 11111111 ->255printf("a=%d,b=%d,c=%d", a, b, c);return 0;
}
3.4.2
int main()
{char a = -128;//10000000 00000000 00000000 10000000 原码//11111111 11111111 11111111 01111111 反码//11111111 11111111 11111111 10000000 补码//10000000 ->-128//按%u: 无符号整形打印,需整形提升//11111111 11111111 11111111 10000000 ->4294967168printf("%u\n", a);return 0;
}
3.4.3
int main()
{char a = 128;//10000000 00000000 00000000 10000000 原码 ->整形提升//11111111 11111111 11111111 01111111 反码//11111111 11111111 11111111 10000000 补码//10000000 ->128//按%u: 无符号整形打印,需整形提升//11111111 11111111 11111111 10000000 ->4294967168printf("%u\n", a);return 0;
}
3.4.4
int main()
{int i = -20;//10000000 00000000 00000000 00010100//11111111 11111111 11111111 11101011//11111111 11111111 11111111 11101100 ->补码unsigned int j = 10;//00000000 00000000 00000000 00001010 ->补码//11111111 11111111 11111111 11101100 +//11111111 11111111 11111111 11110110 ->补码//10000000 00000000 00000000 00001001//10000000 00000000 00000000 00001010 ->10printf("%d\n", i + j);return 0;
}
3.4.5
#include<windows.h>
int main()
{unsigned int i;for (i = 9; i >= 0; i--){printf("%u\n", i);Sleep(1);}return 0;
}
此结果是无限循环,因为i是无符号整形,i的值只可能大于等于0;
3.4.6
int main()
{char a[1000];int i;for (i = 0; i < 1000; i++){a[i] = -1 - i;}printf("%d", strlen(a));return 0;
}
//-1 -2 -3 -4....-128 127 126 125... 0
//-1 -2 -3 -4....-128 127 126 125... 0
//...
strlen遇到\0就结束,并且\0 == 0的,因此在第一次遇到0时就结束计算;
结果是:128+127=255, 不包括\0;
3.4.7
unsigned char i = 0;
int main()
{for (i = 0; i <= 255; i++){printf("hello world\n");}return 0;
}
因为i是无符号字符型,并且i<=255,当i=255时,此时i++的结果是0,因此导致程序死循环;
4.总结
- 正数的原、反、补码都相同。
负整数的三种表示方法各不相同。
- 原码
直接将数值按照正负数的形式翻译成二进制就可以得到原码。 - 反码
将原码的符号位不变,其他位依次按位取反就可以得到反码。 - 补码
反码+1就得到补码。
- 原码 = 补码-1,然后取反;
补充知识:原码 = 补码取反+1; - 对于整形来说:数据存放内存中其实存放的是补码。
大端(存储)模式:把一个数据的低位字节的内容,存放在高地址处,把一个数据的高位字节的内容,存放在低地址处。
大端(存储)模式:把一个数据的低位字节的内容,存放在低地址处,把一个数据的高位字节的内容,存放在高地址处。