本文介绍了一些字、字节、比特、进制、位运算的定义、转换、运算及其使用方法、使用场景、在某些场景下使用二进制的意义。
字、字节、比特。
在计算机领域,"字节"、"字"、"比特"和"位"是基本的数据单位,它们的含义和换算关系如下:
字(Word):
-
字是计算机一次处理的数据单位,大小取决于系统架构:
-
32位系统:1 Word = 4 Bytes = 32 bits
-
64位系统:1 Word = 8 Bytes = 64 bits
-
-
字长决定了CPU一次能处理的数据量
字节(Byte):
-
1 Byte = 8 bits
-
字节是计算机存储和数据处理的基本单位,例如:
-
1个英文字符通常占1字节
-
1个汉字通常占2-3字节(取决于编码)
-
比特(bit)常用:
-
计算机中最小的数据单位,表示一个二进制位,只能是0或1。
-
1 bit = 1个二进制位
-
又名位(bit)与"比特"是同一概念,只是中文翻译不同。比特码通常指用二进制比特(bits) 表示的编码数据。
例如:
-
ASCII 编码中,字母
A的二进制表示是01000001(8 bits = 1 Byte)。 -
颜色 RGB 值可以用 24 bits(3 Bytes)表示,如
FF0000(红色)
进制
十六进制(HEX)
-
基数为 16,使用数字
0-9和字母A-F(或a-f)表示数值。 -
用途:常用于计算机编程、内存地址、颜色代码(如
#FF0000表示红色)。 -
示例:
-
A(十六进制)=10(十进制) -
FF(十六进制)=255(十进制)
-
十进制(DEC)
-
基数为 10,使用数字
0-9,是我们日常使用的标准计数方式。 -
用途:普通数学计算、大多数编程语言中的默认数值表示。
-
示例:
-
15(十进制)=F(十六进制)=1111(二进制)
-
八进制(OCT)
-
基数为 8,使用数字
0-7。 -
用途:早期计算机系统(如 Unix 文件权限
chmod 755),现代编程中较少使用。 -
示例:
-
10(八进制)=8(十进制) -
777(八进制)=511(十进制)
-
二进制(BIN)
-
基数为 2,仅使用数字
0和1。 -
用途:计算机底层数据处理、位运算(如
AND、OR)、硬件编程。 -
示例:
-
1010(二进制)=A(十六进制)=10(十进制) -
11111111(二进制)=FF(十六进制)=255(十进制)
-
换算关系
-
1 Word = 取决于系统架构(通常4或8 Bytes)
-
1 Byte = 8 bits。例如,
01011010是 1 Byte(8 bits)
易混淆点
纳尼纳尼?8bits为啥不是1000而是01011010 呢?
什么是 1 bit?
-
1 bit 是计算机最小的数据单位,只能是
0或1(即一个二进制位)。 -
例如:
0、1都是 1 bit。
所以8位8bits是指8个二进制,是数据长度而不是数据大小。
1Byte = 8 bits=8 个连续的二进制位(如 01011010),取值范围为~
,即二进制的-1000 0000~0111 1111 ,十进制的-128~127。4Byte = 32bits,取值范围为
~
。
电脑计算器中的换算方法(以Windows计算器为例):
-
打开计算器
-
切换到"程序员"模式(Programmer Mode)
-
选择进制(二进制、十进制、十六进制等)
-
输入数值后,计算器会自动显示其他进制的值和比特数

示例换算
-
1 KB (Kilobyte) = 1024 Bytes
-
1 MB (Megabyte) = 1024 KB
-
1 GB (Gigabyte) = 1024 MB
-
1 TB (Terabyte) = 1024 GB
注意:存储设备厂商通常使用十进制换算(1KB=1000Bytes),而操作系统使用二进制换算(1KB=1024Bytes),这会导致标称容量和实际显示容量有差异。
位运算、补码
位运算
| 操作 | 运算符 | 示例(二进制) | 用途 |
|---|---|---|---|
| 按位与 | & | 1100 & 1010 = 1000 | 掩码、清零位 |
| 按位或 | | | 1100 | 1010 = 1110 | |
| 按位异或 | ^ | 1100 ^ 1010 = 0110 | 切换位、交换变量 |
| 按位取反 | ~ | ~1100 = 0011(实际32位) | 取反所有位 |
| 左移 | << | 1100 << 2 = 110000 | 乘以 2^n |
| 算术右移 | >> | 1100 >> 1 = 1110 | 除以 2^n(保留符号) |
| 逻辑右移 | >>> | 1100 >>> 1 = 0110 | 无符号右移 |
位运算直接操作二进制位,以下是 6 种基本操作:
按位与(AND)&
-
规则:两位均为
1时结果为1,否则为0。 -
用途:掩码(Mask)、清零特定位。
int a = 0b1100; // 12 注:0b是java中定义整型数据是byte型数据的方式,一般跟int类型一样占4个字节
int b = 0b1010; // 10
int result = a & b; // 0b1000 (8)
按位或(OR)|
-
规则:两位至少有一个
1时结果为1。 -
用途:设置特定位为
1。
int result = a | b; // 0b1110 (14)
按位异或(XOR)^
-
规则:两位不同时结果为
1,相同为0。 -
用途:切换特定位、交换变量值。
int result = a ^ b; // 0b0110 (6)
// 交换变量
int x = 3, y = 4;
x = x ^ y; y = x ^ y; x = x ^ y; // x=4, y=3
按位取反(NOT)~
-
规则:所有位取反(包括符号位)。
-
注意:结果依赖数据类型位数(如
~0b0001在 32 位中是0xFFFFFFFE)。
int result = ~a; // 0b11111111111111111111111111110011 (-13,32位补码)
左移(Left Shift)<<
-
规则:所有位向左移动,低位补
0。 -
用途:快速乘以
2^n。
int result = a << 2; // 0b11000 (48)
右移
-
算术右移
>>:高位补符号位(负数补1,正数补0)。 -
逻辑右移
>>>:高位补0(无符号右移)。
int c = -8; // 二进制补码0b11111111111111111111111111111000
int arithRight = c >> 1; // 0b11111111111111111111111111111100 (-4)
int logicRight = c >>> 1; // 0b01111111111111111111111111111100 (2147483644)
补码
补码是计算机表示负数的标准方式,其核心规则:
-
负数补码 = 原码取反 + 1
-
补码转原码 = 取反 + 1(与生成补码的步骤相同)
补码与位运算的实战应用
判断奇偶
boolean isOdd = (num & 1) == 1; // 最末位为1则是奇数
取绝对值(避免分支预测)
int abs = (num ^ (num >> 31)) - (num >> 31); // 适用于32位整数
标志位组合
final int FLAG_A = 1 << 0; // 0b0001
final int FLAG_B = 1 << 1; // 0b0010
int flags = FLAG_A | FLAG_B; // 同时设置A和B标志
boolean hasFlagA = (flags & FLAG_A) != 0; // 检查A标志
颜色处理(ARGB 32位)
int alpha = (color >> 24) & 0xFF; // 提取Alpha通道
int red = (color >> 16) & 0xFF; // 提取红色通道
手动计算补码(以 8 位为例)
-
示例:求
-5的补码
-
取绝对值原码:
5→00000101 -
按位取反:
11111010 -
加 1:
11111011(这就是-5的补码)
-
验证:补码转十进制
11111011→ 最高位为1(负数)→ 取反00000100→ 加1→00000101(5)→ 结果是-5。
代码中获取补码
int num = -5;
String binary = Integer.toBinaryString(num); // 输出 "11111111111111111111111111111011"(32位补码)
常见用途
除上述提到的十六进制用于计算机编程、内存地址、颜色代码(如 #FF0000 表示红色),八进制用于计算机系统(如 Unix 文件权限 chmod 755),二进制用于计算机底层数据处理、位运算(如 AND、OR)、硬件编程、位掩码。还有:
-
编程调试:快速查看十六进制或二进制的内存/寄存器值。
-
网络/IP计算:十六进制用于 MAC 地址,二进制用于子网掩码。
-
加密/哈希:分析二进制或十六进制的数据。
-
ASCII 编码:字母
A的二进制表示是01000001(8 bits = 1 Byte)。 -
颜色RGB值:可以用 24 bits(3 Bytes)表示,如 #
FF0000(红色) -
java中的8种数据类型:boolean(1或者4个字节)、int(4个字节)、byte(1个字节)、short(2个字节)、longshort(8个字节)、char(2个字节)、float(4个字节)、double(8个字节)
-
流媒体格式和音视频协议:
-
FLV组成(header8字节/PreviousTagSize4字节/TagHeader11字节)
-
一个字节的NALU头信息(1bit F/2 bit R/5 bit T)
-
ADTS
-
⼀段分辨率为 1920*1080 ,每个像素点为 RGB 占⽤ 3 个字节,帧率是 25 的视频,对于传输带宽的要求是: 1920*1080*3*25/1024/1024=148.315MB/s。
常见数据及其意义
看到这再看平时看到的乱七八糟的数据总看得懂了吧?来来来,搞点实例,现在强得可怕
颜色
#FF0000是十六进制,
-
FF→ 红色(R)通道:最大值(255),表示纯红色。 -
00→ 绿色(G)通道:最小值(0)。 -
00→ 蓝色(B)通道:最小值(0)。
结果:#FF0000 表示 纯红色。
十进制转换:
-
FF(十六进制)=255(十进制) -
00(十六进制)=0(十进制) -
因此,
#FF0000= RGB(255, 0, 0)。
public static final Color PURE_RED = new Color(0xFF0000); // 十六进制
public static final Color PURE_RED_RGB = new Color(255, 0, 0); //十进制
public static final String PURE_RED = "#FF0000"; //字符串
public static final int PURE_RED = 0xFF0000; // 直接存储十六进制值
十六进制颜色值在 Java 中通常以0x开头(而非 #),#仅用于前端表示。如果颜色包含透明度(ARGB),需使用 0xAARRGGBB 格式(如 0xFFFF0000 表示不透明的红色)。
流媒体格式和音视频协议
flv-audio-soundInfo
请说明下面这段代码的意思:
CFlvParser::CAudioTag::CAudioTag(TagHeader *pHeader, uint8_t *pBuf, int nLeftLen, CFlvParser *pParser) {
Init(pHeader, pBuf, nLeftLen);
uint8_t *pd = _pTagData;
_nSoundFormat = (pd[0] & 0xf0) >> 4; //音频格式
_nSoundRate = (pd[0] & 0x0c) >> 2; //采样率
_nSoundSize = (pd[0] & 0x02) >> 1; //采样精度
_nSoundType = (pd[0] & 0x01); //是否是立体声
if(_nSoundFormat == 10) { //AAC
ParseAACTag(pParser);
}
}
先了解一下flv结构

当 tag type = 0x08(音频 Tag) 时,第8个字节存储音频配置信息,其二进制位划分如下:
| 7-4 (4 bits) | 3-2 (2 bits) | 1 (1 bit) | 0 (1 bit) | |---------------|--------------|----------|-----------| | SoundFormat | SoundRate | SoundSize| SoundType |
-
SoundFormat (4 bits): 音频编码格式(如
10=AAC)。 -
SoundRate (2 bits): 采样率(如
3=44.1kHz)。 -
SoundSize (1 bit): 采样精度(
1=16-bit,0=8-bit)。 -
SoundType (1 bit): 声道(
1=立体声,0=单声道)。
具体数字代表的意思如下:

注:UI表示⽆符号整形,后⾯跟的数字表示其⻓度是多少位。⽐如 UI 8 ,表示⽆符号整形,⻓度⼀个字节。UI 24 是三个字节,UI[ 8 *n]表示多个字节。UB表示位域,UB 5 表示⼀个字节的 5 位。
ok,再来看每行代码具体对应的意思:
假设 pd[0] 的值是 0xAE(二进制 10101110),以下是具体解析过程:
(1) 提取音频格式 _nSoundFormat
_nSoundFormat = (pd[0] & 0xF0) >> 4;
-
操作:
-
pd[0] & 0xF0:10101110&11110000=1010000。即:保留高 4 位(0b1010),其余置 0 →0b10100000(0xA0)。即: -
>> 4:右移 4 位 →0b00001010(0x0A)。
-
-
结果:
_nSoundFormat = 0x0A(十进制10),表示音频格式为 AAC(对应上面的FLV -audio-soundInfo表来看 AAC 的编码值就是 10)。
(2) 提取采样率 _nSoundRate
_nSoundRate = (pd[0] & 0x0C) >> 2;
-
操作:
-
pd[0] & 0x0C:保留第 2-3 位(0b00001100),其余置 0 →0b00001100(0x0C)。 -
>> 2:右移 2 位 →0b00000011(0x03)。
-
-
结果:
_nSoundRate = 0x03(十进制3),可能对应 44.1kHz(需查具体协议,FLV 中3通常表示 44.1kHz)。
(3) 提取采样精度 _nSoundSize
_nSoundSize = (pd[0] & 0x02) >> 1;
-
操作:
-
pd[0] & 0x02:保留第 1 位(0b00000010),其余置 0 →0b00000010(0x02)。 -
>> 1:右移 1 位 →0b00000001(0x01)。
-
-
结果:
_nSoundSize = 0x01,表示 16-bit 采样精度(FLV 中1表示 16-bit,0表示 8-bit)。
(4) 提取声道类型 _nSoundType
_nSoundType = (pd[0] & 0x01);
-
操作:
-
pd[0] & 0x01:保留第 0 位(0b00000001),其余置 0 →0b00000001(0x01)。
-
-
结果:
_nSoundType = 0x01,表示 立体声(Stereo)(FLV 中1为立体声,0为单声道)。
flv-tag-header
tag header⼀般占 11 个字节的内存空间。FLV tag结构如下:

解析时位偏移及type类型的对应代码如下:
CFlvParser::Tag *CFlvParser::CreateTag(uint8_t *pBuf, int nLeftLen) {
//开始解析标签头部
TagHeader header;
header.nType = ShowU8(pBuf + 0); //类型
header.nDataSize = ShowU24(pBuf + 1); //标签body的长度,因为type占1个字节,所以数据区偏移1个字节
header.nTimeStamp = ShowU24(pBuf + 4); //时间戳 低24bit,前面加起来4个字节,因此偏移4个字节
header.nTSEx = ShowU8(pBuf + 7); //时间戳的拓展字段 高8bit
header.nStreamID = ShowU24(pBuf + 8); //流的id
header.nTotalTS = (uint32_t)((header.nTSEx << 24)) + header.nTimeStamp;
//标签头部解析结束
cout << "total ts: " << header.nTotalTS << endl;
cout << "nLeftLen:" << nLeftLen << ", nDataSize:" << header.nDataSize << endl;
if((header.nDataSize + 11) > nLeftLen) {
return NULL;
}
Tag *pTag;
switch (header.nType) {
case 0x09: //视频类型的tag
pTag = new CVideoTag(&header, pBuf, nLeftLen, this);
break;
case 0x08: //音频类型的tag
pTag = new CAudioTag(&header, pBuf, nLeftLen, this);
break;
case 0x12: //script tag
pTag = new CMetaDataTag(&header, pBuf, nLeftLen, this);
break;
default:
pTag = new Tag();
pTag->Init(&header, pBuf, nLeftLen);
break;
}
return pTag;
}
用二进制的意义何在?
。。。协议解析真麻烦,对脑子一点也不友好,为什么不直接用十进制呢?
硬件与计算机的本质
-
计算机底层以二进制运行:计算机底层数据处理、硬件编程以二进制。所有数据最终存储为
0和1,直接操作二进制是最高效的方式。 -
CPU 指令原生支持位操作:
&、|、>>等是 CPU 的基础指令,执行速度极快。
节省存储空间
-
紧凑编码:一个字节(8 位)可以存储多个字段(如音频配置的
SoundFormat+SoundRate+SoundSize+SoundType),而用十进制需要多个字节分开存储。// 1 字节存储所有音频参数(二进制掩码) uint8_t audioConfig = (soundFormat << 4) | (soundRate << 2) | (soundSize << 1) | soundType; // 十进制方案需多个变量(占用更多空间) uint8_t soundFormat, soundRate, soundSize, soundType; // 至少 4 字节 -
协议优化:在网络传输或文件存储中,减少 1 字节可能意味着海量数据下的显著带宽/存储节省。
性能优势
-
快速解析:
位操作直接通过硬件指令提取字段,比十进制字符串解析(如atoi)快数十倍。// 高效解析(位操作) _nSoundFormat = (pd[0] & 0xF0) >> 4; // 1-2 条 CPU 指令 // 低效解析(十进制字符串) char str[] = "10,3,1,0"; // 需要分割字符串并转换 sscanf(str, "%d,%d,%d,%d", &format, &rate, &size, &type); // 耗时
明确的位级控制
-
精准定义协议字段:
二进制掩码允许精确控制每个字段的位数和位置,避免歧义。-
例如:
SoundRate严格占用 2 位,强制取值范围0-3,而十进制可能意外传4。
-
-
跨平台一致性:
二进制协议不依赖字符编码(如 ASCII/Unicode),适合异构系统通信。
历史与行业惯例
-
早期硬件限制:
旧系统(如嵌入式设备)资源有限,位操作是唯一可行方案。 -
协议继承性:
现有标准(如 FLV/RTMP/IP 协议)沿用二进制设计,保持兼容性。
十进制方案的缺陷
-
存储冗余:
十进制数字需要额外分隔符(如逗号),增加协议开销。 -
解析复杂度:
字符串处理需考虑分隔符、转义、字符集等问题,易出错。 -
性能瓶颈:
文本解析(如 JSON/XML)比二进制解析慢 10-100 倍,不适合高实时性场景(如视频流)。
二进制 vs 十进制协议
| 特性 | 二进制掩码 + 位移 | 十进制文本协议 |
|---|---|---|
| 存储效率 | 极高(1 字节存多字段) | 低(需分隔符和额外字节) |
| 解析速度 | 纳秒级(CPU 原生支持) | 微秒-毫秒级(需字符串处理) |
| 代码复杂度 | 低(直接位操作) | 高(需分词/类型转换) |
| 适用场景 | 音视频流、网络协议、嵌入式系统 | 配置文件、Web API |
总结
计算机底层以二进制运行,位于或操作都是CPU原生指令,直接操作硬件,操作效率极快。一个字节(8 位)可以存储多个字段,比起十进制算是超级节省存储空间了,在一些高频网络超级大文件流传输过程中,比如音视频流,可以极大的节省带宽,二进制精确控制位级,一个位数一个意义,定好了就不容易出错,遵循一套协议的标准,可实现跨平台操作。
由此可见,二进制适合一些高性能、需求变更不频繁、跟系统、硬件打交道的编程场景。
1万+

被折叠的 条评论
为什么被折叠?



