
用 std::hex 输出整数时为什么总是没前缀、不补零?
因为 std::hex 只改进制,不控制格式——它不会自动加 0x,也不会按位宽补零。你看到的只是纯数字输出,比如 255 变成 ff,而不是 0xff 或 00ff。
要补前缀或补零,得配 std::showbase 和 std::setfill + std::setw:
#include <iomanip>
#include <iostream>
int main() {
int x = 255;
std::cout << std::hex << std::showbase << x << '\n'; // 输出 0xff
std::cout << std::hex << std::showbase << std::setfill('0') << std::setw(4) << x << '\n'; // 输出 0x00ff
}
-
std::showbase才能触发0x前缀,光用std::hex不行 -
std::setw是“一次有效”,下一行输出就得重设,容易漏 - 负数会先转为补码再输出,比如
-1在 32 位 int 下是ffffffff,不是-1
想把 int 转成字符串(如 "ff" 或 "0xFF")该用哪个函数?
别用 sprintf 或手写循环——C++11 起推荐 std::to_string 配合 std::stringstream,或者直接用 C++17 的 std::format(如果编译器支持)。
最稳的跨版本写法是 std::stringstream:
立即学习“C++免费学习笔记(深入)”;
#include <sstream>
#include <iomanip>
std::string to_hex(int n, bool upper = false, bool with_prefix = false, int width = 0) {
std::stringstream ss;
ss << std::hex << (upper ? std::uppercase : std::nouppercase)
<< (with_prefix ? std::showbase : std::noshowbase);
if (width) ss << std::setfill('0') << std::setw(width);
ss << n;
return ss.str();
}
// to_hex(255) → "ff"
// to_hex(255, true, true, 4) → "0X00FF"
-
std::to_string不支持进制转换,传std::hex它也不认 -
std::sprintf容易缓冲区溢出,%x对负数行为未定义(实际常输出补码,但标准不保) -
std::format(C++20)简洁:std::format("{:#x}", 255)→"0xff",但 MSVC 19.3+ / GCC 13+ 才稳定支持
处理无符号类型(uint8_t、size_t)时要注意什么?
有符号整数转十六进制时,高位扩展可能带符号位;而 uint8_t 这类窄类型在流输出时会被提升为 int,导致高位补零失效或显示异常。
典型问题:uint8_t x = 0xff; 直接用 std::cout 可能输出 <code>ffffffff(因为被当成了有符号 int 的 -1)。
- 必须显式转成无符号宽类型,如
static_cast<unsigned int>(x)</unsigned> - 对
size_t,用std::z格式符(C++20)或手动 cast 到unsigned long long再输出 - 用
std::format更安全:std::format("{:x}", static_cast<unsigned>(x))</unsigned>
性能敏感场景下,手写查表法还值得吗?
绝大多数情况不值得——现代标准库的 std::stringstream 或 std::format 已高度优化,且可读性、安全性远胜手工查表。只有在嵌入式或每微秒都算数的场合才考虑。
真要手写,注意两点:
- 查表必须是
static constexpr char table[16] = {'0','1',..., 'f'},否则编译期不折叠 - 逐字节处理时,
uint8_t必须先 & 0xf,避免高位干扰;右移要用无符号右移(>>对unsigned是逻辑右移) - 生成大写还是小写、是否加前缀,都会增加分支,反而拖慢——不如交给标准库统一处理
真正容易被忽略的是:不同平台对负数十六进制的语义理解不一致,哪怕只在日志里打个调试值,也建议统一用无符号转换再输出。










