Base64 编解码中 char 仅作字节容器,必须显式转为 unsigned char 防越界;查表索引须限定 0–63,填充符 = 硬编码追加;解码需用 256 元素 decode_table 预检非法字符。

char 类型足够存 Base64 字符,但别当它是“字符”来用
Base64 映射表里每个符号(A-Z、a-z、0-9、+、/)的 ASCII 值都在 0–127 范围内,char 完全能容纳。但关键不是“能不能存”,而是你是否把它当作有符号整数在参与位运算——这会出错。
比如 char c = '\xff' 在某些平台默认是 -1,后续做 c & 0x3f 就变成 -1 & 0x3f == 0x3f,看似巧合,但一旦查表用 base64_chars[c],负下标直接越界。所以必须显式转成 unsigned char 再索引。
- 查表前一律用
static_cast<unsigned char>(c)</unsigned>或(unsigned char)c - 映射表声明为
static const char base64_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ...";,不加unsigned——它只是字节容器,不是数值 - 不要用
std::string::at()查表,它带边界检查且慢;用[]+ 手动保证索引在 [0,63] 内
用 char 数组实现查表时,索引必须是 0–63 的整数
Base64 编码本质是把 6 位二进制数(0–63)映射成一个 ASCII 字符。char 类型本身不携带“6 位”语义,你得靠位操作从原始字节里抠出这个数。
例如:输入三个字节 a, b, c,要得到第 0 个 Base64 字符,得算 (a >> 2) & 0x3f;第 1 个是 ((a & 0x03) > 4)。这些中间结果必须确保落在 0–63,才能安全喂给 base64_chars[idx]。
- 所有位运算后立刻 & 0x3f,防止高位残留干扰
- 不要依赖编译器对
char的符号扩展行为;显式用uint8_t或unsigned char接收原始输入字节 - 如果用
std::string存原始数据,遍历时仍需static_cast<unsigned char>(c)</unsigned>,因为std::string的operator[]返回char
填充字符 '=' 不是查表来的,是长度规则决定的
= 不在 Base64 映射表里,也不参与位运算。它的出现完全由输入长度模 3 的余数决定:
- 输入长度 % 3 == 0 → 无
= - 输入长度 % 3 == 1 → 补两个
=(原数据只贡献 1 字节,编码后只取前 2 个字符) - 输入长度 % 3 == 2 → 补一个
=(原数据贡献 2 字节,编码后只取前 3 个字符)
也就是说,= 是硬编码追加的,不是从 base64_chars 里取出来的。常见错误是把补零后的临时字节也送进查表逻辑,结果把 \0 当成有效索引,导致乱码或崩溃。
解码时 char 的非法值处理比编码更敏感
编码只输出合法字符,但解码要面对任意输入:空格、换行、小写字母混大写、甚至 _ 和 -(URL-Safe 变种)。这时候不能直接拿 char 当索引。
推荐做法是预建一个 256 元素的 decode_table,用 unsigned char 做下标,内容是 -1(非法)或 0–63(有效索引)。这样 decode_table[(unsigned char)c] 永远安全。
- 初始化表时,对
A-Z、a-z、0-9、+、/分别赋对应值,其余全设为 -1 - 遇到
=不查表,直接按位置判断应丢弃多少字节 - 输入字符串长度不是 4 的倍数,直接返回失败——这是 RFC 4648 的硬性要求,别试图“容错”
最易被忽略的是:解码过程中,哪怕只有一处没检查 decode_table 返回值是否为 -1,整个函数就可能把非法输入当成有效数据继续算下去,最后输出一堆不可预测的垃圾字节。

















