Windows控制台默认用GBK编码导致UTF-8中文输出乱码,根本原因是编码不匹配;需同步设置SetConsoleOutputCP(CP_UTF8)、源文件存为UTF-8无BOM,并编译时添加/utf-8或-finput-charset=UTF-8等参数。

Windows 控制台默认不支持 UTF-8,std::cout 输出中文直接变问号或方块
根本原因不是代码写错了,而是 Windows 控制台(conhost)默认用的是 GBK(CP936)编码,而你的源文件大概率保存为 UTF-8(尤其 VS Code 默认),std::cout 按字节原样输出,系统却用 GBK 解码——字节对不上,就乱码。
实操上最稳的解法是两步同步改:
- 在程序开头调用
SetConsoleOutputCP(CP_UTF8)(需#include <windows.h>),告诉控制台“接下来我输出的是 UTF-8” - 确保源文件存为 UTF-8 **无 BOM**(BOM 会导致
std::cout多输出三个字节,显示异常) - 如果用了
std::wcout,还得额外调用SetConsoleOutputCP(CP_UTF8)+std::wcout.imbue(std::locale("")),但没必要,std::cout配 UTF-8 字符串更轻量
VS / VS Code 编译时没加 /utf-8,字符串字面量本身就被编译器误读
即使控制台编码设对了,如果编译器没被告知源码是 UTF-8,它会把中文字符按当前系统 ANSI 页(如 CP936)解析成错误的字节序列,std::cout 吐出来的根本不是你写的那个 UTF-8 字符串。
对应处理方式:
立即学习“C++免费学习笔记(深入)”;
- Visual Studio:项目属性 → C/C++ → 命令行 → 附加选项里加上
/utf-8 - VS Code + MinGW:在
tasks.json的args中加入-finput-charset=UTF-8 -fexec-charset=UTF-8 - Clang/GCC 10+ 可直接用
-futf-8(但兼容性不如显式指定 charset)
用 std::string 存中文字符串时,别混用 L"中文" 和普通双引号
L"中文" 是宽字符串(wchar_t[]),内存布局和编码都和 "中文"(char[])完全不同。强行传给 std::cout 会触发隐式转换失败,或输出地址值。
正确姿势只有一条路:
- 统一用窄字符串:
std::string s = "你好";(前提是源码存为 UTF-8 且编译器识别它) - 绝对不要写
std::cout —— 这行代码在多数环境下编译不过,或输出乱码/空 - 如果必须用宽字符(比如调 Windows API),那就全程走
std::wcout+SetConsoleOutputCP(CP_UTF8)+std::locale,但控制台字体还得手动设为支持 Unicode 的(如 “Lucida Console” 或 “Consolas”)
Linux/macOS 下基本不用折腾,但跨平台代码仍要防一手
POSIX 系统终端默认就是 UTF-8,std::cout 只要源文件是 UTF-8 就能正常显示。但问题常出在「以为自己写的是跨平台代码」,结果在 Windows 上崩。
稳妥做法:
- 所有字符串字面量保持 UTF-8 编码,不依赖
L""或u8""(后者虽明确但 MSVC 对u8""的支持直到较新版本才稳定) - Windows 平台加条件编译:用
#ifdef _WIN32包住SetConsoleOutputCP(CP_UTF8) - 避免依赖控制台字体——比如用
system("chcp 65001 > nul")切换代码页,看似简单,但会干扰用户原有环境,且子进程不一定继承
真正卡住人的从来不是“怎么让一个例子跑起来”,而是不同环节(编辑器保存、编译器解析、运行时输出、终端解码)各自认的编码不一致。少改一环,就白调半天。










