news 2026/9/17 3:22:55

从bit、byte到大小端与字符编码:计算机存储单位与字节序实战解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从bit、byte到大小端与字符编码:计算机存储单位与字节序实战解析

先说个结论:这些单位本身不复杂,真正让人头大的,是行业习惯、历史遗留和厂商说法混在一起。我刚入行那会儿,买硬盘看标称 500G,拿到手发现只有 465G,以为是商家坑我;后来写 TCP 协议栈,又因为字节序问题 debug 到凌晨。把这些搞明白之后回头看,其实就是几个基础概念和两套换算规则的组合。

这篇文章我会从 bit、byte、字、KB 这些概念的最底层讲起,然后聊到大小端、高低字节、字符编码、带宽估算、结构体字节对齐这些实际场景,最后把常见的坑和排查思路整理成速查表。适合刚接触编程的学生、做数据处理和后台开发的工程师,也适合需要跟服务器、网络、存储打交道的运维同学,看完至少能少走我当年走过的弯路。

1. 先把概念捋清楚:bit、byte、字、KB 到底是什么

这四个词是整套问题的基础,但要命的是,它们在教科书、行业手册和日常口语里的含义有一点点偏差。所以我不按教科书顺序讲,我按“计算机实际干活”的顺序讲。

1.1 bit:最底层的开关

bit 是 binary digit 的缩写,翻译过来叫“比特”或“二进制位”,它表示一个二进制数字:0 或 1。你可以把它想象成一个电灯开关,只有开和关两种状态。

计算机里面所有东西,不管是图片、视频、文本、程序,最终都变成一堆 bit。一个 bit 可以表示 2 种状态,两个 bit 可以表示 4 种状态(00、01、10、11),n 个 bit 就能表示 2 的 n 次方种状态。这就是为什么 8 个 bit 能表示 256 种组合,刚好对应一个字节能放下的无符号数值范围(0 到 255)。

很多朋友听到“在数字信号处理中 h(n) 指什么”这类问题时容易紧张,其实本质就是在说一串离散的数值序列,每个值如果按 bit 存储,就涉及到下面要说的编排方式。这块我们不展开,你只需要记住:bit 是最小的信息单位,没有比它更小的“存储刻度”了。

1.2 byte:计算机世界的最小操作单位

byte 翻译成“字节”,是 8 个 bit 的固定组合。为什么是 8 个?因为早期 IBM 在 1964 年左右推出的 System/360 使用了 8 位一个字节的架构,后来这个标准被广泛接受,一直延续到现在。所以你会看到,1 byte = 8 bit 不是数学推导出来的,是行业标准定的。

字节之所以重要,是因为它是绝大多数计算机硬件寻址和读写的最小单位。意思是,CPU 和内存打交道时,一次最少也要操作一个字节,不能只操作某一个 bit。你写代码的时候倒是可以用位运算去读取某个 bit,但底层内存的最小单元仍然是 byte。

这也是为什么你在 Redis、MySQL 里看到的内存大小、字段长度,基本都以字节为单位。你手机上照片的大小、下载文件的大小,看到的也是字节。bit 在存储领域反而不常见,它更多地活跃在网络带宽、信号处理和底层协议里。

1.3 字(word):跟 CPU“胃口”强相关

“字”这个概念是很多人最陌生的,因为现代电脑用户很少直接在聊天里说“我这个 CPU 一次处理一个字”。但如果你看过 Windows API 里的 WORD、DWORD 类型,或者看过老汇编教材,一定会遇到它。

字(word)指的是 CPU 一次能处理的二进制数据长度,也就是“机器字长”。它在不同硬件上有不同含义:

  • 16 位 CPU 时代,一个字 = 16 bit = 2 byte。
  • 32 位 CPU 时代,一个字通常 = 32 bit = 4 byte。
  • 64 位 CPU 时代,一个字通常 = 64 bit = 8 byte。

这里有个历史雷区:很多教材因为引用 8086/8088 时代的知识,会把“字”固定说成 16 位,然后告诉你“双字”是 32 位,“四字”是 64 位。这个说法在 Windows 的传统类型定义里仍然保留,比如 WORD 是 16 位无符号整数,DWORD 是 32 位无符号整数。但在嵌入式或者信号处理领域,如果你的芯片是 32 位字长,那么“一个字”又确实是 32 位。

所以当你看到“字”的时候,第一反应应该是:这是一次传输或运算的数据粒度,而不是某个固定长度。这正是它和 byte 最大的区别,byte 是固定 8 位,字长取决于硬件。

1.4 KB、MB、GB:怎么换算才不算错

到这里,单位的层级终于排出来了:

  • 1 bit = 1 个二进制位
  • 1 byte = 8 bit
  • 1 KB = 1024 byte(严格说是 1 KiB = 1024 byte)
  • 1 MB = 1024 KB
  • 1 GB = 1024 MB
  • 1 TB = 1024 GB

注意,这里的 1024 是 2 的 10 次方。计算机世界用二进制,所以每一级容量按 2 的幂次增长,1024 是一个很自然的数字。

但现实里还混着另一套十进制体系:1 KB = 1000 byte。这不是乱来,而是国际单位制(SI)的定义。为了区分这两套,IEC 后来搞了 KiB、MiB、GiB 这类名词来专门表示二进制换算(1024 进制),而 KB、MB、GB 在严格意义上被“拨”给了十进制(1000 进制)。可是大家早就用习惯了,绝大多数操作系统、开发工具、数据库里看到的 KB、MB 其实都是二进制含义。于是,混乱就出现了。

后面我会详细说这导致的具体问题,这里你先把两套规则记清楚:

  • 二进制的 KB/MB/GB:进率是 1024,常见于操作系统、内存容量、文件大小、代码里的计算。
  • 十进制的 KB/MB/GB:进率是 1000,常见于硬盘厂商标称容量、通信传输速率的部分场合。

2. 为什么容易搞混:b 和 B、字节与字,这些坑我全踩过

理论概念讲清楚了,接下来才是重点——这些概念在实际工作中是怎么坑人的。

2.1 大小写 b 和 B,差的是 8 倍

先问一个经典问题:你家的宽带是 100M,下载速度为什么最多只有 12.5MB/s 左右?

原因就是,宽带套餐里的 100M 通常写作 100Mbps,这里的 b 是小写,意思是 100Mbit/s,也就是每秒 100 兆比特。而下载工具显示的是 MB/s,B 是大写,意思是每秒多少兆字节。1 byte = 8 bit,所以理论上限就是 100 ÷ 8 = 12.5MB/s,去掉协议开销和线路损耗,实际能有 10MB/s 就不错了。

这个大小写问题,几乎所有做网络、带宽、监控的人都踩过坑。我见过有人部署监控系统,带宽上限明明只有 10Mbps,结果后端程序按 10MB/s 的速度去推流量,直接把交换机打满。也见过有人在购买服务器带宽时,业务峰值要 20MB/s,却按 20Mbps 去估价,结果低估了 8 倍预算。

所以我的建议是:只要看见带“b”的单位,第一反应先确认它是 bit 还是 byte。最稳妥的办法是看单位里的 B 是大写还是小写——B 大写通常是 Byte,b 小写通常是 bit。但要注意某些厂商文档不规范,遇到含糊之处,宁可多问一句。

2.2 硬盘标称容量和系统显示容量为什么不一样

这是一个几乎每个人都会遇见的“规模级”差异。

硬盘厂商按十进制计算容量:1GB = 1,000,000,000 byte。而 Windows、Linux 等操作系统大多按二进制显示:1GB = 1,073,741,824 byte。于是你买一块标称 500GB 的硬盘,系统显示大概只有 465.7GB;买标称 1TB 的,系统显示约 931.5GB。

这个差异不是硬盘坏了,也不是系统 bug,纯粹是“换算基准”不同。厂商标称的 500GB 用的是 10 亿字节,系统显示时除以的是 1,073,741,824,所以数字变小了。你那块“缩水”的容量其实一直都在,只是两种计数刻度不一样。

但我要提醒两句:第一,SSD 和机械硬盘都存在这个现象;第二,U 盘尤其明显,因为很多便宜的 U 盘标称容量甚至还会包含部分隐藏分区、固件区域,实际可用空间更小。

2.3 高低字节和大小端到底是什么关系

字节是存储的最小操作单位,但当多个字节组成一个数值,比如一个 4 字节的 int 数值 0x12345678,涉及到一个存储顺序的问题:内存里哪一段算“高字节”,哪一段算“低字节”?

  • 高字节(high byte):数值中较高权重的字节,比如 0x12。
  • 低字节(low byte):数值中较低权重的字节,比如 0x78。

存储顺序分两种主流派别:

  • 大端序(Big-Endian):高字节在前(低地址存高字节),符合人类阅读习惯。
  • 小端序(Little-Endian):低字节在前(低地址存低字节),符合 CPU 从低地址开始取数据的习惯。

x86 和绝大多数 ARM 都跑小端序,网络协议(TCP/IP 头部、I2C 寄存器地址等)标准一般是大端序,所以才会出现“主机字节序”和“网络字节序”这种说法。做通信编程时,看到套接字(socket)里数据老是不对劲,很多时候不是逻辑错,而是字节序没转换。这个坑我下面专门讲。

3. 从理论到实战:怎么用这些单位解决实际问题

概念和坑都列出来了,现在我们把它们用到真实场景里。

3.1 估算文件大小:一个字符到底占几个字节

平时最常听到的问题就是“一个汉字占几个字节”。这个问题的答案,取决于编码。

  • ASCII 编码:一个英文字母、数字、符号占 1 字节。
  • GBK/GB2312 编码:一个汉字占 2 字节。
  • UTF-8 编码:一个英文字母占 1 字节,大部分汉字占 3 字节,少量特殊字符和 emoji 占 4 字节。
  • UTF-16 编码:一个英文字母占 2 字节,一个汉字大概率也占 2 字节,但某些增补平面字符占 4 字节。

很多人听到“UTF-8 中文占 3 字节”会吓一跳,觉得自己存的文本扩了 50%。确实,如果网站和数据库用 UTF-8,纯中文内容的体积就会比 GBK 大。这也是为什么不少老系统的历史数据用 GBK,后来切 UTF-8 时必须做转码,一个转换不到位,就会变成乱码,也就是热搜里那种“utf 编码不一样字一样”的体验——字面看起来是一个字,底层字节完全不同。

我在做语音转文字、文本导入功能时,就经常遇到这种情况:上游给一个 TXT 文件,说是 UTF-8,结果读出来全是乱码。排查方法很简单:拿十六进制查看器看文件头,UTF-8 带 BOM 的文件开头会有 EF BB BF,UTF-16 LE 是 FF FE。没有 BOM 的话,就用文本工具分别按 UTF-8、GBK 打开,哪种不乱码就是哪种。

如果要在代码里精确统计一个文本占用多少字节,不要用正则去数“字符个数”,要看编码后的字节长度。Python 里就是 len(s.encode('utf-8')),Java 里就是 s.getBytes(StandardCharsets.UTF_8).length。别看这些是基础写法,生产环境里因为统计字节数不对导致数据库字段截断、上报长度超限的情况非常多。

3.2 估算带宽和流量:100M 宽带能跑多少

前面说过,宽带单位是 bit,应用层单位是 byte,换算要除以 8。那“100M 宽带能跑多少”这个问题为什么不能直接除 8?

因为网络传输里还有开销:

  • 以太网帧头、IP 头、TCP 头 / UDP 头。
  • TCP 握手、确认包、窗口滑动机制。
  • 线路干扰和重传。
  • 服务端本身的上传带宽限制。

所以实际可用吞吐量一般是理论值的 80% 到 95%。100Mbps 宽带,跑出 11MB/s 左右已经算健康的;跑到 12.5MB/s 基本是局域网或者极理想环境。

估算流量时还要注意,很多云平台、运营商对“带宽”的计费单位是 Mbps,对“流量”的计费单位是 GB(这里是字节的 GB)。你在购买流量包的时候,500GB 的流量包是 500 千兆字节,不是 500 千兆比特。如果业务侧习惯用“每天产生多少 GB 日志”来描述,你就得先明确他们说的 GB 到底是字节还是比特,否则预算误差能到 8 倍。

3.3 数据库和编程场景:varchar 长度、结构体字节对齐

数据库字段设计是另一个“字节”高频翻车场景。

以 MySQL 为例,varchar(10) 指的是 10 个字符,不是 10 个字节。在 utf8mb4 编码下,一个字符最多占 4 字节,所以 varchar(10) 理论最大能占 40 字节。SQL Server 就不太一样,varchar(10) 是 10 个字节,nvarchar(10) 是 10 个 Unicode 字符(但底层是 UTF-16,通常占 20 字节)。所以你去看“SQL server 字符串转数字”这类问题时,如果字段本身是 varchar 而你要转成 int,一定要先确认里面没有隐藏空格、不可见字符或者超出 int 范围的数字,否则转换必然报错。

再看结构体字节对齐。在 C、C++ 里,结构体成员的排列不是简单地把每个成员长度相加,而是要根据编译器默认对齐数(常见是 4 或 8)进行填充。举例:

struct Example { char a; // 1 byte int b; // 4 bytes char c; // 1 byte };

如果按直觉算,大小应该是 1 + 4 + 1 = 6 字节。但实际在 32 位编译器默认 4 字节对齐下,这个结构体很大概率是 12 字节。为什么?因为 int 要放在“4 的倍数地址”上,编译器会在 a 后面填充 3 个字节,再把 int 放到偏移量为 4 的位置,c 后面还要再补齐 3 个字节,让整个结构体大小成为 4 的整数倍,方便数组排列。

这也解释了为什么很多通信协议在解析二进制帧时,必须使用#pragma pack(1)__attribute__((packed))来取消对齐,否则结构体大小和协议规定的帧长度对不上,数据解析就会偏移错位。你网上搜“结构体字节对齐”时看到的各种大小测试,核心就是这句话:结构体大小不是成员大小之和,而是对齐后的结果。

3.4 通信协议中的字节序:I2C 读写多个字节的例子

嵌入式开发中,字节序问题尤其明显,最具代表性的就是 I2C 读写多个字节的时序。

I2C 协议规定,一次读或写多个寄存器时,地址和数据的字节顺序通常是“高字节在前”。比如一个 16 位寄存器值为 0x1234,你要先发高字节 0x12,再发低字节 0x34。如果你的 MCU 是小端架构,直接用一个 uint16_t 变量按地址强转发送,很可能先发的是 0x34,结果外设读到的寄存器值变成了 0x3412。

解决办法是在驱动层做字节序转换,例如发送前拆分:

uint16_t val = 0x1234; uint8_t hi = (uint8_t)(val >> 8); uint8_t lo = (uint8_t)(val & 0xFF); // 先发 hi,再发 lo

接收时的处理正好相反:

uint16_t val = ((uint16_t)hi << 8) | lo;

这里其实就用到了前面讲的两个概念:高低字节 和 大端小端。你不需要背所有协议的大小端,只需要记住一条经验:凡是跨设备、跨网络传输多字节数值,都要先确认双方规定的字节序;如果设备手册写“big-endian”,而你的 CPU 是小端,就必须手动转换,别指望硬件帮你做。

4. 常见问题与排查技巧实录

这一节整理一些实际工作中常遇到的问题,给出一眼能看懂的排查方向和解决办法。

4.1 “一个汉字占几个字节”为什么答案不一样

场景编码方式一个汉字占用大小说明
传统中文系统GBK/GB23122 字节兼容常见汉字
互联网通吃UTF-83 字节常用汉字,少数生僻字 4 字节
Windows 内部便捷格式UTF-162 字节或 4 字节常用汉字 2 字节,emoji 等 4 字节
简单英文文本ASCII1 字节本身不含汉字

网上流传的“汉字占 2 个字节”之所以有人反驳,是因为他们拿 UTF-8 环境举例。你说它错吗?在 GBK 下没错。你说它对吗?在 UTF-8 下就不对。以后有新人再问,先反问他用什么编码,再给答案。

4.2 字符串转数字时遇到的字节陷阱

很多编程题和实际开发都会涉及“字符串转数字”或“数字转字符串”。表面上是用 parseInt、atoi、strtol 之类,但底层和字节关系密切。

典型坑点:

  • 字符串含不可见字符:比如从文件读来的字符串带\r结尾,atoi可能直接解析失败或者解析出错误结果。
  • 溢出:一个 4 字节的 int 最大是 2,147,483,647,如果字符串表示的数字超过这个范围,C 语言的atoi是未定义行为,Java 的Integer.parseInt会抛异常。
  • 字符编码和数字混淆:字符串 “123” 在 ASCII 里是0x31 0x32 0x33,不是整数 123。你把单个字符直接转 int,得到的通常是它的 ASCII 码(49、50、51),而不是 1、2、3。这个错误在单片机解析串口数据时特别常见。

4.3 SQL Server 里字符串转数字报错

SQL Server 里执行CAST('123' AS INT)本来很简单,但实际报错多半是这几个原因:

  • 字符串里有空格或引号,CAST不会自动忽略。
  • 字符串里有千位分隔符,比如1,234
  • 字符串表示小数,但目标类型是INT
  • 字符串超出目标类型范围,比如99999999999999999999BIGINT

排查时先执行SELECT LEN(column), ASCII(LEFT(column,1))之类的手段把异常字符找出来,再决定是REPLACE还是LTRIM(RTRIM(...))。这本质上也是字节层面的问题——你以为你看到的字符和数据库存储的字符是同一个,实际上可能夹杂了看不见的字节。

4.4 结构体字节对齐导致的结构体大小超预期

排查思路很简单:

  1. 先打印sizeof(struct name),看实际大小。
  2. 再打印每个成员的偏移量,用offsetof宏查看。
  3. 找出哪一个成员偏离了“成员大小之和”最多,那个位置就是填充字节所在。
  4. 如果这个结构体要用作网络报文、文件记录、共享内存,那就加#pragma pack(1)或者用__attribute__((packed))强制紧凑排列。
  5. 如果结构体是用于高性能计算,反而建议不要随便取消对齐,未对齐访问在某些 CPU 上会导致性能暴跌,甚至触发硬件异常。

我也见过很多人把“结构体大小是成员大小之和”当默认结论去算协议头部长度,结果每 20 字节的包头被编译器悄悄加到了 24 字节,整个协议解析错位,查了一天最后发现是对齐问题。

4.5 关于“高低字节”的快速判断技巧

在日志里看到数值不对时,我一般先画一张小表:把数值展开成十六进制,比如 0x12345678,左边是高字节,右边是低字节。然后看内存 dump 或者协议抓包里第一个出现的字节是高还是低,就能判断是大端还是小端。

还有个小技巧:用复杂数判断字节序时,看单个数字 0x01020304 在内存里的第一个字节。如果内存第一个字节是 01,就是大端;如果是 04,就是小端。这个技巧在调试网络协议、I2C、SPI、串口二进制帧时都能快速用上。

5. 个人经验:把单位关系刻进工程习惯里

这些基础概念听起来像常识,但真正影响效率的,是你在工程设计时有没有把它们当成第一优先级。

我的习惯是,在项目文档里明确写下“本系统所有存储单位默认是字节,所有传输带宽单位默认是比特”,并在接口文档、数据库设计文档里统一标注bytebit。这样至少能避免团队成员之间因为单位换算而产生 8 倍的误解。

另外,在解析外部数据、网络报文、二进制文件时,我建议第一步先把字节序和字段长度画成一张表格,标注每个字段的字节偏移、长度、大小端、有无符号。等把这张表做出来,再写代码就不容易出错了。我这些年排查过的诡异 bug,相当一部分都源于“没先对齐字段定义”。

最后再分享一个我自己踩过的坑:有段时间做短信 PDU 编码,短信内容按 GBK 编码后每条最长 70 个汉字,按 UCS-2 编码是 140 字节,按 UTF-8 编码可能超过 140 字节,导致短信被拆条。大家讨论“一条短信能发多少字”时,如果不说清楚编码和字节单位,结论就会差出好几倍。所以,别小看“字节、字、bit、byte、KB”这些最基础的单位,它们几乎影响着你写的每一行和“大小”相关的代码。把关系理清后再看什么带宽、存储、协议、数据库字段,你会觉得整个世界都清晰了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/17 3:21:51

OPC一人公司不只是“一人一智能体”,更是企业组织OPC化

OPC一人公司不只是“一人一智能体”&#xff0c;更是企业组织OPC化一、一个需要被澄清的误解很多人第一次听到“OPC一人公司”&#xff0c;会下意识地理解为&#xff1a;“一个人&#xff0c;加一个智能体&#xff0c;就是一家公司。”这个理解没有错&#xff0c;但它只说对了一…

作者头像 李华
网站建设 2026/9/17 3:21:38

MyBatis自定义TypeHandler查询JSON字段返回null的排查与解决方案

如果你在 MyBatis 里自定义了一个 TypeHandler 来处理 MySQL 的 JSON 字段&#xff0c;结果查询返回 null&#xff0c;先别急着怀疑人生。这个问题我在项目里至少见过十几次&#xff0c;每次排查到最后原因都很简单&#xff0c;但没踩过坑的人就是看不出来。所谓“自定义 TypeH…

作者头像 李华
网站建设 2026/9/17 3:18:29

火电机组协调控制Simulink高保真建模与工程落地

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华