位运算存日期比结构体更节省内存:32位整数可分配11位年(0–2047)、4位月(0–15)、5位日(0–31),共20位,远低于32位上限,几百万条记录可显著减少内存占用和提升缓存效率。

为什么不用结构体而要用位运算存日期
结构体存 year、month、day 看似直观,但会占用至少 12 字节(比如 int×3),而一个 32 位整数刚好能塞下完整日期——2025 年前的年份只需 11 位(0–4095),月份 4 位(0–15),日 5 位(0–31),加起来才 20 位,远低于 32 位上限。关键是:当你要存几百万个日期(比如日志时间戳、数据库索引字段),省下的内存和缓存友好性是实打实的。
怎么分配这 32 位并避免越界
常见错误是直接用 (year ,这会把 <code>year 左移 16 位,但若 year 是 2024,二进制已超 11 位,再左移就可能溢出或覆盖其他字段。正确做法是先截断再移位:
-
year取低 11 位:(year & 0x7FF)(0x7FF= 2047,足够覆盖 1970–2170) -
month限 1–12,取低 4 位:((month & 0xF) -
day限 1–31,取低 5 位:((day & 0x1F) - 最终组合:
((year & 0x7FF)
注意位偏移顺序:这里把 year 放最低 11 位(bit 0–10),month 在 bit 11–14,day 在 bit 15–19。你可以按需调换,但提取时必须严格对应。
提取字段时最容易漏掉符号扩展
如果用 int32_t 存储,而你用 (date >> 11) & 0xF 提月,看起来没问题;但如果 date 是 int 类型且高位为 1(比如某天编码后最高位被置 1),右移可能触发算术右移,导致 month 提出来是负数。安全做法是统一用无符号类型操作:
uint32_t date = ...; uint8_t year = date & 0x7FF; uint8_t month = (date >> 11) & 0xF; uint8_t day = (date >> 15) & 0x1F;
别用 int 做位运算容器,也别在提取后直接赋给 signed char——哪怕值在 1–12 范围内,编译器也可能悄悄补符号位。
验证合法性不能只靠位宽
位运算是“信什么就存什么”,它不会拒绝 month=13 或 day=32。所以写入前必须校验:
-
year建议限定范围,比如1970 -
month必须1 -
day需结合年月判断,比如闰年 2 月最多 29 天——位字段本身不携带闰年信息,这部分逻辑得额外实现
也就是说,位压缩只是存储层,业务校验和日期计算仍要走独立逻辑。别指望靠位掩码自动拦住 2024-02-30 这种非法值。
真正麻烦的是跨平台读取:不同 CPU 的字节序不影响位域布局(因为是纯逻辑移位),但如果你把 uint32_t 写进文件再用另一台机器读,只要都用相同位定义,就没问题。怕的是有人手写宏定义时把位偏移写反了,或者提取时忘了 & 掩码——这种 bug 不报错,只让日期莫名其妙变成 1970-01-01。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











