std::hex和std::oct会永久改变cout的整数进制输出格式,直至显式切回(如std::dec);它们影响整个流状态而非单次输出,易导致后续调试混乱,需谨慎管理或改用C++20的std::format避免状态污染。

用std::hex和std::oct切换进制输出
默认情况下cout以十进制输出整数,要改输出进制,得靠std::hex、std::oct这些流操纵器(manipulator)。它们不是函数调用,而是直接插入到cout表达式中起作用的特殊对象。
关键点:它们会**永久改变后续输出的整数进制格式**,直到你再次显式切回其他进制(比如std::dec)。
常见错误是只设一次hex就以为只影响下一行——其实会影响之后所有整数输出,容易导致后续调试输出错乱。
示例:
立即学习“C++免费学习笔记(深入)”;
#include <iostream>
using namespace std;
int main() {
cout << 255 << " "; // 输出:255
cout << hex << 255 << " "; // 输出:ff(小写)
cout << 16 << " "; // 输出:10(仍为十六进制!)
cout << dec << 16 << "\n"; // 输出:16(切回十进制)
}
控制大小写、前缀和填充宽度
std::hex默认输出小写字母,加std::uppercase可转大写;想让0x或0前缀自动出现,得配合std::showbase;若需固定位宽(比如始终输出4位),要用std::setw和std::setfill。
注意:std::setw只对**下一个输出项**生效,而std::showbase、std::uppercase是持久状态。
-
cout << showbase << hex << 255;→ 输出0xff -
cout << showbase << uppercase << hex << 255;→ 输出0XFF -
cout << setw(4) << setfill('0') << hex << 255;→ 输出00ff(注意:setw不与showbase联动,0x不算在4位内)
八进制输出的特殊陷阱
std::oct本身没有大小写问题,但容易和**字面量前缀混淆**:C++里0177这种带前导零的整数字面量默认就是八进制,但cout << oct << 177;输出的是261(因为177是十进制数,转成八进制是261)。
常见误操作:
- 把
0177当成十进制传给oct,结果输出意外的177(其实是八进制177 = 十进制127,再转八进制还是177) - 忘记
oct是持久的,后面输出时间戳、计数器等整数全变成八进制,debug时一头雾水 -
showbase对八进制生效后输出0前缀,但这个0和C风格八进制字面量的0纯属巧合,语义不同
更安全的替代方案:用std::format(C++20)
如果项目支持C++20,std::format能避免流状态污染问题,每个输出独立控制格式:
#include <format>
#include <iostream>
int main() {
std::cout << std::format("{:x}", 255) << "\n"; // ff
std::cout << std::format("{:#x}", 255) << "\n"; // 0xff
std::cout << std::format("{:04x}", 255) << "\n"; // 00ff
std::cout << std::format("{:o}", 177) << "\n"; // 261(177是十进制)
}
缺点是目前编译器支持度不一(GCC 13+、Clang 15+、MSVC 19.30+),且不能完全替代cout的流式习惯。老项目里还是得小心管理hex/oct的状态。
最易被忽略的点:进制操纵器影响的是**整个流对象的状态**,不是单次调用。哪怕你在函数里临时改了cout的进制,返回后主流程也可能被波及——除非你明确恢复(比如cout << dec)或用std::ostringstream隔离。


















