按位取反本质是~对每个字节(char)逐位翻转(0→1、1→0),结果为等长字节序列,非加密;适用于ascii单字节场景,会破坏utf-8结构;两次取反可完全还原,但输出需用十六进制或base64避免乱码截断。

字符串按位取反的本质就是 ~ 运算符作用于每个字节
按位取反不是加密,只是对每个 char 做 ~c(即 8 位补码取反),结果仍是 char,但可能变成不可见或控制字符。C++ 中直接对 std::string 的每个元素用 ~ 即可,注意符号扩展问题:如果 char 是有符号类型(常见于 x86 Linux/macOS),~c 会先提升为 int,再截断回 char,行为正确;但若想避免依赖实现,显式转成 unsigned char 更稳妥。
-
std::string存储的是字节序列,不关心编码,所以 UTF-8 字符会被拆开取反,导致彻底乱码——仅适用于 ASCII 或已知单字节编码场景 - 不要对空字符串跳过处理,
~对\0得到\xFF,这是合法字节,无需特殊判断 - 示例:
std::string obfuscate(const std::string& s) { std::string out = s; for (unsigned char& b : out) { b = ~b; } return out; }
解混淆必须用相同逻辑,且不能用 std::cout 直接打印结果
混淆后字符串大概率含 \x00~\x1F 等非打印字符,用 std::cout 会提前截断(遇到 <code><p>混淆后字符串大概率含 <code>\x00~\x1F 等非打印字符,用 std::cout 会提前截断(遇到 <code>\0)或输出乱码。调试时应逐字节查看十六进制值,或用 printf 格式化输出:
printf 格式化输出:
- 解混淆函数和混淆函数完全对称:
for (unsigned char& b : out) b = ~b;
(两次取反等于还原) - 验证是否还原成功,别用
==比较原始字符串和“打印出来看起来一样”的字符串,而要用std::equal(s1.begin(), s1.end(), s2.begin()) - 打印混淆后内容的可靠方式:
for (unsigned char b : data) printf("%02x ", b);
Windows 控制台默认不显示非 ASCII 字节,SetConsoleOutputCP(65001) 也救不了
即使把混淆后字符串当作 UTF-8 输出,Windows 控制台仍会拒绝渲染非法 UTF-8 序列(而取反后的字节几乎必然破坏 UTF-8 结构)。这不是编码设置问题,是数据本身已不是有效文本。强行设置代码页只会让输出更不可预测。
- 在 Windows 上,
std::wcout和SetConsoleOutputCP对混淆后数据无效——它们只影响解释方式,不改变字节内容 - 真正安全的调试手段只有:写入二进制文件用十六进制编辑器查看,或用
std::hex+std::setw(2)输出每个字节 - 若必须在终端观察,可 base64 编码混淆结果:
std::string b64 = base64_encode(obfuscate("hello"));,这样全是可见 ASCII
混淆后字符串长度不变,但 std::string::c_str() 不再以 \0 安全结尾
std::string 内部存储包含 \0 是完全合法的,但调用 c_str() 返回的 C 风格字符串仍以第一个 \0 为终止符——如果混淆后某字节恰好是 0(比如原字符是 \xFF),那 c_str() 就会提前截断。这不是 bug,是 C 字符串语义决定的。
- 任何需要传给 C API(如
printf("%s", s.c_str())、open()路径)的场景,都必须确认混淆后字符串不含\0,否则行为未定义 - 若需传递完整字节流,改用
s.data()+s.size(),而不是c_str() - 混淆前检查是否含
\0没用——取反后\0变成\xFF,但\xFF取反又变回\0,所以原字符串是否含\0不影响结果是否含\0
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











