系列文章
文章目录
信息的表示和处理
-
计算机使用二值信号存储和表示信息(bit)
-
三种最重要的数字表示:
·基于传统的二进制表示法的无符号编码
·常用于表示有符号整数的补码编码
·以2为基数来表示实数的浮点数编码 -
当计算结果太大以至于不能表示时,就会产生溢出。
-
浮点数表示的精度有限,浮点运算是不可结合的。
浮点数由于其精度有限,运算时可能会产生舍入误差。所以浮点运算是不具有结合律的,即
(𝑎+𝑏)+𝑐不一定等于 𝑎+(𝑏+𝑐) -
整数的表示范围小但精确,浮点数表示的范围大但是是近似的。
2.1信息存储
-
大多数的计算机使用8位组成一个字节,作为最小的可寻址的内存单位。尽管计算机处理器可能在更大块(如字、双字)的基础上进行操作,但内存的基本单位是字节。
-
机器级程序不包含关于数据类型的信息:
在机器级别(也称为汇编级别),程序指令只处理原始的二进制数据,不区分数据的类型。数据类型的解释由高级编程语言和编译器来处理的,在机器代码中,所有数据只是字节序列。 -
指针的值是某个存储块的第一个字节的虚拟地址,虚拟地址是一个逻辑地址,由操作系统和硬件共同管理,通过内存管理单元(MMU)映射到物理内存地址。
-
每个程序对象可以视为一个字节块,程序本身就是一个字节序列
2.1.1 十六进制表示法
使用16个符号来表示数值:0-9表示0到9,A-F表示10到15。十六进制表示法的优势在于它与计算机内部使用的二进制表示法之间的转换非常方便。
假设我们有一个十六进制数 0x2F3,我们可以将其转换为二进制数:
0x····2·····𝐹·····3
= 0010·1111·0011
0x2F3=001011110011
将十六进制数转换为十进制数,可以使用权重法,将十进制数转换为十六进制数,可以使用除基取余法。(现实中使用百度法和熟能生巧法)
2.1.2 字数据大小
-
每个计算机有对应的字长,由虚拟地址空间大小决定,所以字长决定了虚拟地址空间的大小。(总线传输的字是单次传输单位的位数,这里的字长是地址空间的位数)
-
32位机器的虚拟地址空间最高为4GB,64 位字长的虚拟地址空间最高16EB,所以32位机器的指针类型长度为4字节,64 位机器的指针类型长度为 8 字节。(现代64位处理器通常并不会使用完整的64根地址线,够用就行)
-
**int32_t 和 int64_t **类型分别为 4 字节和 8 字节,不受机器影响。使用确定大小的整数类型很有用。对 32 位和 64 位机器而言,char、short、int、long long 长度都是一样的,为 1,2,4,8。long 的长度不一样。float 和 double 的长度一样,分别为 4,8
-
char 类型可以是有符号的(signed)或无符号的(unsigned),取决于编译器的实现和具体的使用场景。大多数情况下,程序对 char 是有符号还是无符号并不敏感,除非进行明确的数值比较或运算。
-
使用大小确定的数据类型可以提高程序的可移植性
2.1.3 寻址和字节顺序
-
跨越多字节的对象,其地址是它所使用字节中的最小地址。(起始地址)
-
大端法和小端法
小端法:数字的低位字节存储在内存的低地址处。
大端法:数字的高位字节存储在内存的低地址处。
例如,对于一个32位整数 0x12345678
小端法的存储顺序如下:
地址: 0x00 0x01 0x02 0x03
数据: 0x78 0x56 0x34 0x12
大端法的存储顺序如下:
地址: 0x00 0x01 0x02 0x03
数据: 0x12 0x34 0x56 0x78
-
大多数 Intel 系统使用小端法,理解字节序对于编写跨平台和网络通信程序非常重要。小端法和大端法的选择会影响到数据的存储和传输方式,在涉及多字节数据时,必须明确系统和协议的字节序。通过编写检测字节序的程序,可以确保数据在不同系统之间的一致性和正确性。
-
检测当前系统的字节序
#include <stdio.h>
int main() {
unsigned int x = 0x12345678;
//前面提过,取出的一定是最小的地址,也就是低地址
char *c = (char*)&x;
if (*c == 0x78)
printf("小端机\n");
if (*c == 0x12) {
printf("大端机\n");
return 0;
}
2.1.4 表示字符串
-
C 语言字符串是以 null 字符结尾的字符数组,即 ‘\0’
-
文本数据比二进制数据具有更强的平台独立性
-
ASCII 字符适合英文文档。Unicode(UTF-8)使用 4 字节表示字符,常用的字符只需要 1 或 2 个字节。所有 ASCII 字符在 UTF-8 中是一样的。JAVA 使用 UTF-8 来编码字符串。(C语言也有支持UTF-8的库函数)
2.1.5 表示代码
-
二进制代码是不兼容的,很难在不同机器和操作系统组合下进行移植。
-
从机器的角度看,程序就是一个字节序列。机器没有关于原始源程序的任何信息。
2.1.6 布尔代数
-
布尔代数是在 0 和 1 基础上的定义,主要用于处理二值变量(真(True)和假(False))。
布尔变量:布尔变量取值仅为 True 或 False 的变量,通常用 1 表示 True,用 0 表示 False。 -
布尔运算:布尔代数中的基本运算包括:
与(AND):也称为逻辑乘积,用符号 ∧ 或 · 表示。只有当两个操作数都为 True 时,结果才为 True。
或(OR):也称为逻辑和,用符号 ∨ 表示。只要有一个操作数为 True,结果就为 True。
非(NOT):也称为逻辑否定,用符号 ¬ 或 ! 表示。将 True 变为 False,将 False 变为 True。 -
把字节看作是一个长为 8 的位向量,我们可以对字节进行各种位运算
2.1.7 C 语言中的位级运算
-
C语言支持按位布尔运算
-
按位与(AND)运算符:&
用于将两个数的每一位进行逻辑与运算。
只有当两个对应位都为 1 时,结果位才为 1,否则为 0。 -
按位或(OR)运算符:|
用于将两个数的每一位进行逻辑或运算。
只要有一个对应位为 1,结果位就为 1,否则为 0。 -
按位异或(XOR)运算符:^
用于将两个数的每一位进行逻辑异或运算。
当两个对应位不同,结果位为 1;相同,结果位为 0。 -
按位取反(NOT)运算符:~
用于将一个数的每一位进行取反运算。
0 变为 1,1 变为 0。
2.1.8 C 语言中的逻辑运算
-
** 逻辑与**(AND)运算符:&&
只有当两个操作数都为真时,结果才为真。
如果第一个操作数为假,则不再计算第二个操作数(短路求值)。 -
在这个例子中,由于 a 为 0,表达式 a && (b / a > 1) 中的第二个条件 (b / a > 1) 不会被计算,从而避免了除零的错误。
#include <stdio.h>
int main()
{
int a = 0;
int b = 5;
if (a && (b / a > 1)) printf("ture\n");
else printf("false\n");
return 0;
}
-
逻辑或(OR)运算符:||
只要有一个操作数为真,结果就为真。
如果第一个操作数为真,则不再计算第二个操作数(短路求值)。 -
逻辑非(NOT)运算符:!
将操作数的布尔值取反。
如果操作数为真,结果为假;如果操作数为假,结果为真。
2.1.9 C 语言中的移位运算
- 左移运算符(<<)
左移运算符将一个数的所有位向左移动指定的位数,右边补 0。 - 右移运算符(>>)
右移运算符将一个数的所有位向右移动指定的位数。右移时,左边补 0(逻辑右移)或符号位(算术右移)。