1. 进制

在基数为 R 的进制中,包含 R 个数字符号, 分别是 0, 1, 2, …, R-1 一个整数n位、小数m位的R进制数字,可以表示为:

(N)R=(Kn−1Kn−2...K0.K−1K−2...K−m)R(N)_{R} = (K_{n-1} K_{n-2} ... K_0.K_{-1}K_{-2}...K_{-m})_{R}

其中,KiK_i 是第 ii 位上的数字符号,满足 0≤Ki<R0 \leq K_i < R。 同时,也可以用连加符号来表示:

(N)R=∑i=−mn−1KiRi(N)_{R} = \sum_{i=-m}^{n-1} K_i R^i


1.1 二进制

二进制是基数为 2 的进制系统,使用两个数字符号:0 和 1。每一位上的权重是 2 的幂次方。

如:

(1101)2=1∗23+1∗22+0∗21+1∗20=8+4+0+1=(13)10(1101)_2 = 1 * 2^3 + 1 * 2^2 + 0 * 2^1 + 1 * 2^0 = 8 + 4 + 0 + 1 = (13)_{10}


1.2 八进制、十六进制与二进制的换算

八进制和十六进制是计算机常用的进制。

  • 八进制(Oct,简写O):使用八个数字符号:0, 1, 2, 3, 4, 5, 6 和 7。
  • 十六进制(Hex,简写H):使用十六个数字符号:0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E 和 F。

由于 8=238 = 2^3 和 16=2416 = 2^4,八进制和十六进制的每一位可以直接对应二进制的三位和四位:

八进制:
0 = 0b000  1 = 0b001  2 = 0b010  3 = 0b011  
4 = 0b100  5 = 0b101  6 = 0b110  7 = 0b111

十六进制:
0 = 0b0000  1 = 0b0001  2 = 0b0010  3 = 0b0011  
4 = 0b0100  5 = 0b0101  6 = 0b0110  7 = 0b0111  
8 = 0b1000  9 = 0b1001  A = 0b1010  B = 0b1011  
C = 0b1100  D = 0b1101  E = 0b1110  F = 0b1111

2. 比特、字节与字

  • 比特(bit):计算机的最小数据单位,只有两个取值:0或1
  • 字节(Byte):一个字节由8个比特组成,即1Byte = 8bit
  • 字(Word):计算机进行数据处理和存储时,作为一个单元的一组二进制数位。
    • 字的长度(即一个字包含的比特位数)取决于计算机的硬件架构。早期16位计算机系统中,一个字是16比特位,在现在的32位和64位计算机系统中,一个字分别是32比特和64比特。
    • 计算机的 CPU 在处理数据时,通常以字为单位进行操作。较大的字长意味着 CPU 一次能够处理更多的数据,从而提高计算机的运算速度和效率

3. 内存与地址

  1. 内存是计算机中用于暂时存储数据和程序的部件,当你打开一个程序,程序及其相关数据就会被加载到内存中,CPU 才能对它们进行处理。
  2. 内存地址:内存地址是内存中每个存储单元(每个字节)的唯一编号,CPU通过地址获取数据存储在内存中的位置,进而进行读写操作。

注:对于32位系统,由于地址总线宽度为 32 位,我们用 32 个二进制位来表示地址,通常写成十六进制也就是 0x00000000 到 0xFFFFFFFF 的形式。类似地,64位系统用 64 个二进制位来表示地址,通常写成十六进制也就是 0x0000000000000000 到 0xFFFFFFFFFFFFFFFF 的形式。

C++的内存结构: C++的内存结构


3.1 C++进程的内存布局

  • 代码段 (.text):编译后的机器指令、函数代码,只读共享
  • 常量段 (.rodata):字符串字面量、const全局常量,只读
  • 全局 / 静态数据段 (.data/.bss)
    • .data:初始化全局变量、静态局部变量
    • .bss:未初始化全局 / 静态变量,程序启动清零
  • 堆 (Heap)
  • 共享库、内存映射区域
  • 栈 (Stack)(每个线程独立栈)
  • 内核空间(用户程序不可访问)

其中,堆和栈是程序运行时动态分配的内存区域。堆用于动态分配内存,从低地址往高地址增长;而栈用于函数调用和局部变量的存储,从高地址往低地址增长;两者中间留大片空闲虚拟内存,避免碰撞。


3.2 栈的管理规则

  1. 操作系统预先分配固定大小:Windows 默认 1MB,Linux 默认 8MB,编译链接时可改栈大小
  2. 自动生命周期:进入函数分配栈帧,函数 return 自动销毁栈帧,无需手动释放
  3. 分配极快:只修改栈寄存器 rsp/sp,无系统调用
  4. LIFO 后进先出,栈帧连续内存,无碎片
  5. 缺点:空间极小,大数组、深度递归会触发 栈溢出 Stack Overflow

3.3 堆的管理规则

  1. 空间巨大:使用进程剩余虚拟内存,上限由操作系统 / 物理内存限制
  2. 手动管理:
    • C:malloc 申请,free 释放
    • C++:new 申请,delete 释放;忘记释放造成内存泄漏;重复释放、野指针崩溃
  3. 分配慢:需要堆管理器查找空闲块、维护链表元数据,频繁分配释放产生内存碎片
  4. 生命周期不受函数限制:函数退出堆内存不会自动销毁,必须手动释放
  5. 进程全局共享,所有线程共用同一块堆,并发分配需要加锁,存在竞争