直接用 std::string 异或会出错,因为异或结果可能含 '\0',导致被误作c字符串而截断;应使用 data()+length() 操作字节,密钥需循环展开且可复现,加解密可用同一函数实现。

为什么直接用 std::string 异或会出错?
很多人一上来就写 for (auto& c : s) c ^= key;,结果发现加密后字符串变短、乱码、甚至崩溃。根本原因是:异或结果可能为 \0(ASCII 0),而 C++ 的 std::string 虽然允许包含 \0,但若后续被误当作 C 风格字符串(比如传给 printf 或某些旧接口),就会在第一个 \0 处截断。
- 务必用
std::string::data()+length()操作原始字节,别依赖c_str() - 密钥不能是单个
char—— 否则所有字符用同一值异或,等同于凯撒密码,毫无安全性 - 动态密钥必须可复现:加解密时密钥序列必须完全一致,否则无法还原
怎么生成可复现的动态密钥流?
“动态”不等于随机,而是指每个字符用不同密钥字节异或。最常用且安全的方式是用密钥字符串循环展开,长度对齐明文:
- 密钥串如
"abc"对明文"hello"(5 字节),生成密钥流:'a','b','c','a','b' - 用
key[i % key.length()]实现循环,简单高效,无状态依赖 - 避免用
std::rand()或时间戳做密钥流——加解密环境时间不同步或种子不一致就彻底失败 - 如果需要更高强度,可用
std::hash<:string>{}(salt + key)</:string>生成初始向量,再配合简单 PRNG,但多数场景没必要
加解密函数怎么写才不出 bug?
加解密本质是同一个操作:异或两次等于原值。所以一个函数就能搞定,关键在参数和边界处理:
std::string xor_crypt(const std::string& data, const std::string& key) {
if (key.empty()) return data; // 防止除零
std::string out = data;
for (size_t i = 0; i
- 输入
data和key都按const std::string&传,避免拷贝开销 - 不要修改原
data,而是构造新std::string并逐字节赋值 ——out[i]安全,out.at(i)会检查越界但慢 - 密钥为空时直接返回原串,而不是抛异常或断言 —— 生产代码要容错
- 注意:该函数既可用于加密也可用于解密,只要密钥相同,
xor_crypt(xor_crypt(s,k),k) == s
二进制数据和中文字符串能用吗?
可以,但必须明确一点:C++ 的 std::string 是字节容器,不是“文本容器”。UTF-8 编码的中文在内存里就是多个字节,异或操作只管字节,不管语义。
- 加密前无需转码,UTF-8 字节流直接异或即可;解密后也仍是合法 UTF-8(只要密钥一致)
- 但切勿对
std::u16string或std::u32string直接套用此逻辑 —— 它们内部是宽字符,字节序和长度都不同 - 如果加密内容含图片、音频等二进制数据,同样适用,只是注意保存/传输时别当文本处理(比如别用
std::ofstream默认文本模式) - 真正容易翻车的地方是:把加密后的
std::string当作普通字符串打印 —— 里面大量不可见字符和\0,终端显示为空或乱码是正常现象
密钥流的复现性比“看起来像密码”重要得多;只要密钥串一致,哪怕全是数字或控制字符,也能正确加解密。别在密钥生成逻辑里埋时间、线程 ID 这类不可控变量。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











