[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. 算法步骤 (字节到位的转换)
这是一个概念性转换,可以描述为以下步骤:
- 确定字节值: 获取一个字节的值,范围是0-255。例如,十进制数
13。 - 转换为二进制: 将这个十进制数转换为一个8位的二进制数。
-
13 ÷ 2 = 6余16 ÷ 2 = 3余03 ÷ 2 = 1余11 ÷ 2 = 0余1- 倒序取余数得到
1101。
- 补足8位: 在左边用0补足,直到总共有8位。
00001101。 - 结果: 字节
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 运算符,编写一个程序,输出你的系统中 short, int, long, long 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