1 一切皆bits

计算机是二进制的,所以每一比特位都只能取值0或1。所有的数字都是基于二进制在计算机中表示。
8位bit的组合被称为字节byte。不同的数据类型有不同的长度,同一类型在不同架构的系统里基于的byte数也会不同:

C Data Type Typical 32-bit Typical 64-bit x86-64
char 1 1 1
short 2 2 2
int 4 4 4
long 4 8 8
float 4 4 4
double 8 8 8
long double 10/16
pointer 4 8 8

1.1 整型的大小端字节序

  • 小端序:低位字节保存在低位内存地址,简称低低
    • x86,arm处理器都是小端序
  • 大端序:低位保存在高位内存地址,高位字节保存在低位内存地址,简称低高
    • internet大端序

例如对于数值0x01234567,由于二进制从右往左表示数值,所以12是高位字节,67是低位字节,所以大小端的显示方式如下:
image.png
下面代码示例可以判断系统是什么字节序:

  1. #include <iostream>
  2. typedef unsigned char *pointer;
  3. //该函数用于打印输入数值的内存地址和对应的值
  4. //由此可以看出是什么字节序
  5. void show_bytes(pointer start, size_t len)
  6. {
  7. size_t i;
  8. for (i = 0; i < len; i++)
  9. printf("%p\t 0x%.2x\n", start + i, start[i]);
  10. printf("\n");
  11. }
  12. int main()
  13. {
  14. int a = 15213; //16进制位0x3b6d
  15. show_bytes((pointer)&a, sizeof(int));
  16. return 0;
  17. }
  18. /*
  19. 输出为:可以看出,符合低低原则,是小端序
  20. 0x7ffe4715f0a4 0x6d
  21. 0x7ffe4715f0a5 0x3b
  22. 0x7ffe4715f0a6 0x00
  23. 0x7ffe4715f0a7 0x00
  24. */

1.2 字符串的表示

字符串是一系列有序的字符组成,以\0结束,在内存中总是以相同的顺序保存,和大小端字节序没有关系。
例如对字符串char S[6] = "18213",不管系统是什么字节序,内存中表示都是:
image.png

2 位级运算

2.1 位运算

单个bit之间有如下四种基本运算,下面是四种运算的基本图示:
image.png
二进制数之间的运算也是根据每个bit的上述运算结果组合而成。

note:在C/C++中,可以使用&、|、~、^来表示这四种运算。但是位运算符需要与逻辑运算符号区分开,判断符号有&&、||、!等。

2.2 位移运算

  • 左移 x<<y:
    • 二进制数x向左移动y个bit
    • 左侧超出范围的bit被丢弃
    • 右侧空白位补0
  • 右移 x >>y:
    • 二进制数x向右移动y个bit
    • 右侧超出范围的bit被丢弃
    • 左侧空白位补0

2 信息的表示和处理 - 图4

3 整型表示

3.1 数值范围计算

  • 无符号:不需要考虑符号位,0 至(2 -1)
  • 有符号:需要考虑符号位,-2 至 2 - 1

    当前计算机中,有符号数基本都采用补码的方式进行表示

例如常见类型的数值范围:
image.png

3.2 符号扩展

将一个小范围的数据类型转换到一个大的数据类型时(比如short int 到int),C/C++会自动进行符号的扩展:
image.png

  • 无符号转为更大的数值:前面扩展位填充0,原bit位值不变
  • 有符号转为更大的数值:前面扩展位填充原来符号位的值(0或1),原bit位值不变

    4 浮点数

    4.1 单精度和双精度的表示

    IEEE浮点数表示标准:
    2 信息的表示和处理 - 图7

  • 符号(S):当s=1为负数,当s=0为正数;

  • 尾数(M):表示从(1~2)或者(0~1)之间的数;
  • 阶码(E):可以是负数

其内存表示如下:
image.png

4.2 浮点数的数学属性

浮点数使用的是向偶数舍入的方法,为了避免统计上的误差,在一半的时间向下舍入,另一半的时间向上舍入。由于舍入而产生的丢失精度,浮点数的运算中不具有结合性。
因此在C/C++中使用浮点数需要注意:

  • int转换为float时,数值不会溢出,但是可能被舍入,导致和原int值不一致
  • int或float转换为double时,因为double数值范围更大,精度更高,因此可以保留精确的数值
  • 从double转换为float时,因为范围减小,数值可能溢出为极大或极小(+∞ or -∞ ),并可能被舍入
  • 从float或double转换为int时,值会向0舍入。进一步来说,值可能会溢出(C标准没有规定这种情况的处理结果,如果不能为浮点数找到一个合理的整数近似值,就会产生一个整数不确定值
    • 比如1.999转换为1,-1.999转换为-1