0. 这篇文章解决什么问题

当你在看性能、网络协议、文件格式、C/C++ 数据结构、对齐与缓存时,最常见的“基础卡点”就是两件事:

  • 单位:bit/byte 怎么换?KB/MB/GB 到底是 1000 还是 1024?
  • 类型占用int/float/double/指针到底占多少字节?为什么同一份代码在不同机器/编译器下会不一样?

本文目标:把这些变成一张“随手查”的速查表 + 一套“在代码里验证”的方法。


1. bit 与 byte:最基础的两条规则

  • 1 byte = 8 bit
  • **bit(b)**常用于带宽/速率:例如 100 Mb/s(兆比特每秒)
  • **byte(B)**常用于容量/大小:例如 100 MB(兆字节)

1.1 常见误读

  • Mb 不是 MB:前者是 megabit,后者是 megabyte
  • 带宽宣传常用 十进制(1000 体系),而操作系统文件大小显示有时更接近 二进制(1024 体系)或直接标为 GiB/MiB

2. KB/MB/GB:十进制与二进制两套体系

2.1 十进制(SI)单位:常见于磁盘/网络厂商标注

  • 1 KB = 1000 B
  • 1 MB = 1000 KB = 10^6 B
  • 1 GB = 1000 MB = 10^9 B
  • 1 TB = 10^12 B

2.2 二进制(IEC)单位:常见于内存/操作系统语境

  • 1 KiB = 1024 B = 2^10 B
  • 1 MiB = 1024 KiB = 2^20 B
  • 1 GiB = 1024 MiB = 2^30 B
  • 1 TiB = 2^40 B

2.3 速查表(建议截图收藏)

名称 十进制 二进制
1 KB / 1 KiB 1000 B 1024 B
1 MB / 1 MiB 1,000,000 B 1,048,576 B
1 GB / 1 GiB 1,000,000,000 B 1,073,741,824 B

2.4 一个特别常用的换算:Mbps 到 MB/s

由于 1\text{ Byte}=8\text{ bit},所以:

  • \text{MB/s} \approx \text{Mb/s} \div 8 (先不考虑协议开销)

例子:

  • 100 Mb/s ≈ 12.5 MB/s
  • 1 Gb/s ≈ 125 MB/s

3. “类型占用”到底由什么决定

你看到的 sizeof(T) 主要取决于:

  • 目标平台:尤其是 32 位 vs 64 位(指针大小差异最大)
  • 编译器 ABI:例如 LP64、LLP64 等数据模型
  • 类型本身的规范:例如 IEEE-754 浮点(float 常见 4B,double 常见 8B)
  • 对齐与填充:结构体里会为了对齐插入 padding,导致“字段相加 ≠ sizeof(struct)”

4. 常见 C/C++ 基础类型占用(经验值)

4.1 “几乎所有现代平台都这样”的部分

类型 常见大小 说明
char 1 B 标准只保证 sizeof(char)==1
float 4 B 多数平台 IEEE-754 binary32
double 8 B 多数平台 IEEE-754 binary64

4.2 容易变化的部分(一定要以 sizeof 为准)

下表是“常见 ABI 下的经验值”,不是语言标准承诺。

类型 32 位(常见) 64 位(Linux/macOS 常见 LP64) 64 位(Windows 常见 LLP64)
short 2 B 2 B 2 B
int 4 B 4 B 4 B
long 4 B 8 B 4 B
long long 8 B 8 B 8 B
指针 T* 4 B 8 B 8 B
size_t 4 B 8 B 8 B

5. 指针“占用 8 字节”到底意味着什么

以 64 位平台为例,T* 常见 8 字节,代表:

  • 它只是一个地址值的存储空间(能表示更大的虚拟地址范围)
  • 不等于“它指向的对象占用 8 字节”
  • 指针相关的常见误区:
    • sizeof(p) 是指针大小(4 或 8),不是数组/对象大小
    • sizeof(*p) 才是它指向的类型大小
    • 数组在表达式里常“退化”为指针,但 sizeof(arr) 仍然是数组总字节数(在同一作用域里)

6. 在代码里自检:一段最小可运行模板

把下面这段放到任意 C++ 工程里跑一下,你会立刻得到“你当前环境”的真实答案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <cstdint>
#include <iostream>

int main() {
std::cout << "sizeof(char) = " << sizeof(char) << "\n";
std::cout << "sizeof(short) = " << sizeof(short) << "\n";
std::cout << "sizeof(int) = " << sizeof(int) << "\n";
std::cout << "sizeof(long) = " << sizeof(long) << "\n";
std::cout << "sizeof(long long) = " << sizeof(long long) << "\n";
std::cout << "sizeof(size_t) = " << sizeof(size_t) << "\n";
std::cout << "sizeof(void*) = " << sizeof(void*) << "\n";
std::cout << "sizeof(float) = " << sizeof(float) << "\n";
std::cout << "sizeof(double) = " << sizeof(double) << "\n";

std::cout << "sizeof(int32_t) = " << sizeof(int32_t) << "\n";
std::cout << "sizeof(uint64_t) = " << sizeof(uint64_t) << "\n";
}

如果你写的是 C,也可以用 printf("%zu\n", sizeof(...)) 输出。


7. 结构体的大小:对齐(alignment)与填充(padding)

7.1 一个典型例子

1
2
3
4
5
struct A {
char c; // 1
int i; // 4
short s; // 2
};

直觉相加:1+4+2=7 字节,但实际 sizeof(A) 往往是 12(也可能是 8/16,取决于 ABI 与对齐规则)。

原因:多数平台要求 int 按 4 字节对齐,编译器会在 c 后面插入 padding;结构体整体也会 padding 到最大对齐倍数,方便数组元素对齐。

7.2 实用建议

  • 要“确定布局”,优先用固定宽度整数:uint32_tint16_t
  • 序列化/网络协议不要直接 memcpy(struct)(除非你完全控制 ABI、对齐、端序,并且写了静态断言)

8. float 与 int:除了大小,还差在“表示范围与精度”

这篇以“占用”为主题,但你通常也会顺带踩到:

  • int32_t离散整数,每个值精确
  • float浮点近似,很多小数无法精确表示

建议你在后续文章(或本篇扩展)加入:

  • float 的有效精度约 6~7 位十进制
  • double 的有效精度约 15~16 位十进制
  • 典型坑:0.1 + 0.2 != 0.3(比较时用误差阈值)

9. 可选扩展(你可以按需要补充)

  • 9.1 端序(Endianness):网络字节序与本机字节序
  • 9.2 缓存行(cache line):结构体布局对性能的影响
  • 9.3 malloc/new 与对象头:实际内存占用不等于 sizeof(T)
  • 9.4 对齐控制alignas#pragma pack(以及它们的风险)

10. 小结(可直接作为结尾)

  • bit/byte 是 8 倍关系,KB/MB/GB 存在 10001024 两套体系
  • int/long/指针 的大小会随平台 ABI 变化,最稳妥的方式是:sizeof 验证 + 用固定宽度类型表达协议/文件格式
  • 结构体的真实占用要考虑 对齐与填充