无符号编码:基于传统的二进制表示法,表示大于或等于0的数字
补码:有符号整数的常见方式(可正可负)
浮点数:表示实属的科学计数法的以2为基数的版本
整数的运算满足整数运算的许多性质但浮点运算有完全不同的数学属性。
信息存储
- 字节作为最小的可寻址的内存单位。
- 内存可视为一个非常大的字节数组,称为虚拟内存。内存中的每个字节都由一个唯一的数字来表示,称为地址。所有可能地址的集合称为虚拟地址空间。
- 每个程序对象可视为一个字节块,而程序本身就是一个字节序列。
- C语言的指针有两个方面:值和类型。值表示某个对象的位置,类型表示那个位置上所存储对象的类型。
1.1 十六进制表示法
1.1.1 二进制、十进制和十六进制的一般表示法
- 窍门:记住A、C、F相应的十进制值,而其他的值可以根据这三个值的相对关系来计算得出。
- 对于二进制转化为十六进制,先将二进制数以4位为一组进行分割,若二进制位总数不是4的倍数,则最左边的一组可少于4位,缺少的位数用0补全。然后再将每个4位转换为对应的十六进制数字即可。
- 对于各种进制之间的相互转换,除k取余法均适用。注意,除k取余法得到的余数,余数需从下往上取出,并从左往右写出结果
- 十进制加减法使用的方法适用于十六进制,只是十进制以10为基数,十六进制以16为基数。(即满十进一改成满十六进一,数字加10变成加16,运算方式不变)
1.1.2 对于2的非负整数次幂的十六进制转换法
1.2 字数据大小
- 字长指明了指针数据的标称大小,字长决定的系统参数是虚拟地址空间的大小。
对于一个字长为w位的机器来说,虚拟地址范围为0 ~ ,程序最多访问 个字节(即虚拟地址空间的大小)。
比如,32位字长的机器,虚拟地址空间为4千兆字节(4GB),64位字长的机器,虚拟地址空间为16GB 。64位机器可以运行32位机器编译的程序,但是用64位机器编译的程序就只能在64位机器上运行。因此,32位程序和64位程序,区别在于程序如何编译,而不是运行的机器类型。
-
C语言中各种数据类型分配的字节数如图:
- 整数有符号,可以表示负数、零和正数,无符号时,只能表示非负数。
- int32_t和int64_t,分别是4个字节和8个字节,是确定大小的整数类型
- 单精度(float)和双精度(double)分别使用4字节和8字节
1.3 寻址和字节顺序
- 两个规则:这个对象的地址是什么,在内存中如何排列这些字节
对象的地址是什么
多字节对象都被存储为连续的字节序列,对象的地址为所使用字节中最小的地址。
内存中如何排列字节
- 小端法:最低有效字节在最前面的方式
- 大端法:最高有效字节在最前面的方式
此处注意最高/低有效字节和最前面的定义。比如一个字0x01234567中,最高有效字节为0x01,最低有效字节为0x67。"最前面"的意思是指,地址从小到大计算,较小的一端为"前面"。如在地址0x100~0x103中存放上述的数据,0x100为"前面"。其大端法和小端法的字节排列如下图:
Android和IOS两种操作系统采用的是小端法。
字节顺序带来的问题:
采用不同端法的机器通过网络传输二进制数据时,字节会变成反序的。解决办法是相关的网络应用程序按照已建立的字节顺序的规则,发送方将它的内部表示转换成网络标准,接收方将网络标准转换成接收方的内部表示。
当阅读表示整数数据的字节序列时,字节顺序的不一样会导致书写的字节顺序和读取字节顺序相反
-
当编写 规避了正常的类型系统 的程序时,会带来问题。如 通过强制类型转换,或联合来允许一种数据类型来引用一个对象,而这种数据类型于创建这个对象时定义的数据类型不同。如图所示:
此处将byte_pointer定义成一个指向类型为unsigned char对象的指针。当调用show_bytes(int x)方法时,程序将x的指针&x,强制转换成unsigned char*。这样得到的转换值,将会被程序看成指向一个字节序列,而不是指向一个原始数据类型int的对象。然后,这个被强制转换的指针,将会被看成是对象使用的最低字节地址。
1.4 表示字符串
- C语言中字符串被编码成一个以null(值为0)字符结尾的字符数组。如字符串"12345",其ASCII码为31 32 33 34 35 00,该 字符串长度为6,包含终止符。
- 十进制数字x的ASCII码是0x3x,如数字1的ASCII码是0x31,2的ASCII码是0x32等。
- 字母'a'-'z'的ASCII码范围是0x61~0x7A。
1.5 表示代码
- 不同的机器类型使用不同的且不兼容的指令和编码方式。即使时完全一样的进行,运行在不同操作系统的编码规则会有不同,因此得到的二进制代码时不兼容的。
1.6 布尔代数
- 布尔运算 ~ 对应逻辑运算NOT,命题逻辑符号 。当P非真时, P为真。
- 布尔运算 & 对应逻辑运算AND,命题逻辑符 。当P、Q均为真时,P Q为真。
- 布尔运算 | 对应逻辑运算OR,命题逻辑符 。当P或Q为真时,P Q成立。
- 布尔运算 ^ 对应逻辑运算异或,命题逻辑符 。当P或Q为真,但不同时为真时,P Q成立
- 加法逆元属性:每个值x都存在一个加法逆元-x,使得x+(-x)=0。在布尔运算中的加法是 ,每个元素的加法逆元是它本身,即a ^ a=0。另外还有(ab)a=b的特点。
位向量
- 位向量是固定长度为 ,由0和1 组成的串。即一串比特。
- 应用:
- 表示有限集合,存在记为1,不存在记为0。
- 对集合进行编码。在计算机中可以有选择地使能或屏蔽一些信号。
1.7 C语言中的位级运算
C语言中通常对十六进制数进行位运算。如0x69&0x55, ~0x41等。确定这样一个位级表达式的结果的办法,就是将十六进制的参数扩展成二进制并执行二进制运算,然后再转换回十六进制。
-
第二个用法,实现掩码运算。例子如图所示:
1.8 C语言中的逻辑运算
- C语言中的 || 、&&、!分别对应于命题逻辑中的OR、AND、NOT。逻辑运算认为所有非0参数都为TRUE,参数0表示FALSE,逻辑运算返回1或0。
- 按位运算和逻辑运算的区别:
- 按位运算是逐个比特位进行逻辑运算,逻辑运算是整个数表示为0或1 才进行运算。按位运算只有参数被限制为0或1 时,才与逻辑运算有相同的行为。 比如0x69 && 0x55 ,两个参数都会认为是1,所有这个与逻辑运算的结果是1。
- 逻辑运算的第一个参数求值就能确定表达式结果时,就不会对后面的参数进行求值。比如, a&&5/a,就不会出现除数为0的情况。
1.9 C语言中的移位运算
设操作数为x,一共有 位,移动的位数为k,0 k -1。
- 左移运算:x向左移动k位,丢弃最高的k位,并在右端补k个0。左移运算是从左至右可结合的。如 x<<j<<k (x<<j) <<k。
- 右移运算:分逻辑右移和算术右移。逻辑右移在左端补k个0,算术右移在左端补k个最高有效位的值。比如参数x=10010101,逻辑右移4位,得0000 1001;算术右移4位,得1111 1001。
总结:在移位运算中,只有算术右移在移位时补充最高有效位得值,其他移位运算都是补0。几乎所有机器都对有符号数使用算术右移,而对于无符号数,使用的是逻辑右移。
C语言中并没有明确定义有符号数该用哪种类型的右移。在Java中则有明确定义,x>>k表示将x算术右移k位,x>>>k表示对x逻辑右移k位。
当位移k很大时怎么办
如 int val = 0xFEDCBA98 >> 36,k=36,大于val的位数(32位),则此时计算k mod =4,则对val右移4位。这种行为C语言中没有保证,在Java中的位移数量则会按照这个方法来计算。
另外,在C语言中,加减法运算的优先级比移位运算的优先级要高。