信奥崔老师:位、字节与字

224 阅读7分钟

[1] 位、字节与字

图片

1. 概念介绍

在计算机内部,所有信息——无论是数字、字符还是指令——最终都以二进制(0和1)的形式存储和处理。位、字节和字是衡量这些二进制数据大小的基本单位。

  • 位 (bit):  是计算机中数据的最小单位。一个位只能表示两种状态:0或1。英文 bit 是 "binary digit"(二进制数字)的缩写。
  • 字节 (Byte):  是计算机中数据处理和存储的基本单位。1字节等于8位 (1 Byte = 8 bits)。一个字节可以表示 2^8 = 256 种不同的状态。通常用大写 B 表示。
  • 字 (Word):  是计算机进行数据处理和运算的单位,其长度(字的位数)称为字长。字长取决于CPU的设计,常见的有32位(4字节)和64位(8字节)。一个字长的整数是CPU处理起来效率最高的。

单位换算:

  • 1 Byte = 8 bits
  • 1 KB (Kilobyte) = 1024 B
  • 1 MB (Megabyte) = 1024 KB
  • 1 GB (Gigabyte) = 1024 MB
  • 1 TB (Terabyte) = 1024 GB

2. 算法步骤 (字节到位的转换)

这是一个概念性转换,可以描述为以下步骤:

  1. 确定字节值:  获取一个字节的值,范围是0-255。例如,十进制数 13
  2. 转换为二进制:  将这个十进制数转换为一个8位的二进制数。
    • 13 ÷ 2 = 6 余 1
    • 6 ÷ 2 = 3 余 0
    • 3 ÷ 2 = 1 余 1
    • 1 ÷ 2 = 0 余 1
    • 倒序取余数得到 1101
  3. 补足8位:  在左边用0补足,直到总共有8位。00001101
  4. 结果:  字节 13 就由 0``0``0``0``1``1``0``1 这8个位组成。

3. 算法可视化svg图示 (一个字节的构成)

图片

4. 核心特性

  • 二进制基础:  计算机所有数据的底层表示都是二进制位。
  • 固定大小:  一个字节总是由8个位组成。
  • 编址单位:  内存通常是按字节进行编址的,每个字节都有一个唯一的内存地址。
  • 与CPU相关:  字的大小(字长)直接关联CPU的架构(32位或64位),影响其单次处理数据的能力。

5. C++代码基础实现

C++提供了 sizeof 运算符来获取一个数据类型或变量占用的字节数。

#include <iostream>
#include <climits> // 用于获取CHAR_BIT

int main() {
    // 1. 使用sizeof获取各种类型占用的字节数
    std::cout << "一个 char 占用 " << sizeof(char) << " 字节" << std::endl;
    std::cout << "一个 int 占用 " << sizeof(int) << " 字节" << std::endl;
    std::cout << "一个 long long 占用 " << sizeof(long long) << " 字节" << std::endl;
    std::cout << "一个 double 占用 " << sizeof(double) << " 字节" << std::endl;

    // 2. 验证1字节等于多少位 (通常是8)
    // CHAR_BIT 定义在 <climits> 中,表示一个char类型的位数
    std::cout << "\n在本系统中,1字节 = " << CHAR_BIT << " 位" << std::endl;

    // 3. 演示一个char如何存储0-255之间的值
    unsigned char my_byte = 255// unsigned char 范围是 0-255, 正好一个字节
    std::cout << "无符号字符型变量 my_byte 的值是: " << (int)my_byte << std::endl// 需要强制转换为int才能正确打印数值

    return 0;
}

6. 优化策略

在算法竞赛中,利用位运算可以直接对二进制位进行操作,通常比常规的算术运算速度更快,且能节省空间。

  • 位掩码 (Bitmask):  使用一个整数的各位来表示一组集合的状态。
  • 位运算:
    • & (按位与): 用于提取特定位。
    • | (按位或): 用于设置特定位。
    • ^ (按位异或): 用于翻转特定位或进行无进位加法。
    • ~ (按位取反): 翻转所有位。
    • << (左移): x << n 相当于 x * 2^n
    • >> (右移): x >> n 相当于 x / 2^n (对于正数)。 这些是高级技巧,但在入门阶段了解其概念对理解计算机底层工作有益。

7. 优缺点

  • 优点:
    • 底层性:  是理解计算机存储、数据结构对齐、性能优化的基础。
    • 高效性:  基于位的操作非常快速。
    • 空间效率:  可以用一个位来表示一个布尔状态,极大地压缩存储空间(例如使用 std::bitset 或位掩码)。
  • 缺点:
    • 抽象层次低:  直接操作位比较繁琐,可读性差,容易出错。
    • 不可移植性:  依赖于位序(大端/小端)和字长的代码可能在不同架构的机器上表现不一。

8. 应用场景

  • 数据压缩:  通过更高效的位编码来减少文件大小。
  • 加密算法:  许多加密过程都涉及到复杂的位操作。
  • 图形学:  颜色表示(如ARGB),图像处理。
  • 状态表示:  在算法中用一个整数的各位来紧凑地表示多个开关状态(状态压缩DP)。
  • 硬件编程/嵌入式开发:  直接控制硬件寄存器的特定位。

9. 扩展

  • 大端与小端 (Endianness):  多字节数据(如int)在内存中存储的顺序。
    • 大端 (Big-Endian):  高位字节存放在低地址。
    • 小端 (Little-Endian):  低位字节存放在低地址。(x86架构的PC机普遍使用小端)
  • 补码 (Two's Complement):  现代计算机表示负数的方式。一个数的补码是其按位取反后加一。
  • 浮点数的位表示 (IEEE 754): float 和 double 类型在内存中是如何用位来表示符号、指数和尾数的。

10. 5个课后配套练习及C++代码实现答案

练习1:  计算 2 KB 等于多少位?C++ 代码 (计算并验证):

#include <iostream>

int main() {
    long long kilobytes = 2;
    long long bytes = kilobytes * 1024;
    long long bits = bytes * 8;
    std::cout << "2 KB = " << kilobytes << " * 1024 Bytes" << std::endl;
    std::cout << "     = " << bytes << " Bytes" << std::endl;
    std::cout << "     = " << bytes << " * 8 bits" << std::endl;
    std::cout << "     = " << bits << " bits" << std::endl;
    return 0;
}

答案:  2 * 1024 * 8 = 16384 位。

练习2:  使用 sizeof 运算符,编写一个程序,输出你的系统中 shortintlonglong long 四种整数类型分别占用多少字节。C++ 代码实现:

#include <iostream>

int main() {
    std::cout << "sizeof(short): " << sizeof(short) << " bytes" << std::endl;
    std::cout << "sizeof(int): " << sizeof(int) << " bytes" << std::endl;
    std::cout << "sizeof(long): " << sizeof(long) << " bytes" << std::endl;
    std::cout << "sizeof(long long): " << sizeof(long long) << " bytes" << std::endl;
    return 0;
}

(注:long 的大小在不同系统下可能不同,在64位Windows下通常是4字节,在64位Linux下通常是8字节)

练习3:  一个 int 类型变量在32位系统上通常占用多少位?C++ 代码 (计算并验证):

#include <iostream>
#include <climits>

int main() {
    int bits_of_int = sizeof(int) * CHAR_BIT;
    std::cout << "在一个 int 占用 " << sizeof(int) << " 字节的系统中," << std::endl;
    std::cout << "它占用了 " << bits_of_int << " 位。" << std::endl;
    return 0;
}

答案:  在32位系统上,int 通常是4字节,所以是 4 * 8 = 32 位。

练习4: unsigned char 类型的最大值为什么是255?请从位的角度解释。答案: unsigned char 占用1个字节,即8个位。当这8个位全部为1时(11111111),它能表示的数最大。这个二进制数转换为十进制就是 2^7 + 2^6 + 2^5 + 2^4 + 2^3 + 2^2 + 2^1 + 2^0 = 128 + 64 + 32 + 16 + 8 + 4 + 2 + 1 = 255。

练习5:  用C++代码展示如何使用位运算将一个整数 x 的第3位(从0开始计数)设置为1,而不改变其他位。C++ 代码实现:

#include <iostream>
#include <bitset>

int main() {
    int x = 10// 二进制为 00001010
    std::cout << "原始数字: " << x << " (" << std::bitset<8>(x) << ")" << std::endl;

    // 1. 构造一个只有第3位是1的数 (1 << 3)
    // 1 的二进制是 00000001
    // 1 << 3 左移3位后是 00001000
    int mask = 1 << 3;
    std::cout << "掩码 (mask): " << mask << " (" << std::bitset<8>(mask) << ")" << std::endl;

    // 2. 使用按位或(|)操作
    x = x | mask;

    std::cout << "设置第3位后: " << x << " (" << std::bitset<8>(x) << ")" << std::endl;
    // 00001010 | 00001000 = 00001010 (结果应为18)
    
    return 0;
}

11. 相关网络资源推荐

  • OI Wiki - 位运算
  • 菜鸟教程 - C++ 位运算符
  • YouTube - Bitwise Operators - Computerphile