Windows 下应优先使用 SetConsoleTextAttribute 配合 GetStdHandle 获取句柄设置颜色,避免硬编码句柄或参数;Linux/macOS 须运行时检测终端支持再用 ANSI 转义,并重置颜色;跨平台应动态检测环境而非依赖编译宏,且颜色语义需用枚举隔离。

Windows 下用 SetConsoleTextAttribute 改颜色最直接
Windows 控制台原生支持文字颜色,SetConsoleTextAttribute 是唯一靠谱的 API,不用第三方库、不依赖 ANSI 转义(旧版 cmd 默认不解析)。它直接改当前句柄的前景/背景色,生效快、无兼容风险。
常见错误是没获取控制台句柄就调用,或传错参数导致颜色乱码甚至崩溃:
- 必须先调
GetStdHandle(STD_OUTPUT_HANDLE)拿句柄,不能硬写0或NULL - 颜色值是位掩码组合:
FOREGROUND_RED | FOREGROUND_INTENSITY表示亮红色,不是简单数字 12 - 每次输出前都要设一次,颜色不会自动继承到下一行(除非你手动保存/恢复)
示例:输出红字
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(hOut, FOREGROUND_RED | FOREGROUND_INTENSITY);
printf("出错了\n");
// 记得恢复默认色,否则后续所有输出都红
SetConsoleTextAttribute(hOut, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
Linux/macOS 用 ANSI 转义序列,但得确认终端支持
Unix 系统靠 \033[31m 这类转义控制颜色,本质是发控制字符给终端解释。问题在于:某些环境(如 VS Code 集成终端旧版本、CI 日志管道、重定向到文件)会把转义符当普通文本输出,显示成 ^[[31m 这种乱码。
立即学习“C++免费学习笔记(深入)”;
- 先检查
isatty(STDOUT_FILENO),非终端输出时跳过颜色,避免污染日志 - 不要手写
\033,用\x1b更清晰,也避免部分编译器警告 - 背景色和前景色混用要小心:
\x1b[41;37m是白字红底,但41和37顺序无关;而\x1b[0m必须结尾重置,否则影响后续命令行提示符
示例:绿色成功提示
#include <unistd.h>
if (isatty(STDOUT_FILENO)) {
printf("\x1b[32m[ OK ]\x1b[0m 已加载配置\n");
} else {
printf("[ OK ] 已加载配置\n");
}
C++ 跨平台封装时,别硬写宏开关,优先检测运行时环境
很多人用 #ifdef _WIN32 分支写两套逻辑,结果在 Windows Subsystem for Linux(WSL)里跑 SetConsoleTextAttribute 失败,或在新版本 Windows Terminal 里 ANSI 被禁用却没 fallback。
- 运行时检测比编译时更可靠:查
getenv("TERM")是否含xterm、linux,或调GetConsoleScreenBufferInfo看是否真有控制台句柄 - Windows 10 1511+ 默认开启 ANSI 支持,但需调
SetConsoleMode(hOut, ENABLE_VIRTUAL_TERMINAL_PROCESSING)才生效,否则\x1b[被忽略 - 避免封装成“一键设色”函数——颜色状态是全局的,多线程同时输出可能互相覆盖,真要安全就得加锁或用线程局部缓冲
颜色值别硬编码,用枚举或常量隔离语义
写 31 或 FOREGROUND_RED 本身没问题,但一旦出现 31 表示错误、32 表示成功、33 表示警告这种映射,很快就会在日志里看到黄字被当成警告、红字被当成错误——因为没人记得清哪个数字对应什么业务含义。
- 定义
enum class TextColor { Error = 31, Success = 32, Warning = 33 };,强制类型检查 - Windows 下用
WORD值封装,比如static constexpr WORD RED_ON_BLACK = FOREGROUND_RED;,和 ANSI 的31解耦 - 深色/浅色终端背景下同一颜色可读性差(比如黄字在黄色背景上消失),真要健壮就得读取系统主题或让用户配色方案,而不是只靠固定值
这事没标准解法,越想“通用”,越容易在某个终端、某次升级后突然失效。颜色只是辅助,别让它变成 bug 来源。










