为何C++中多字节UTF-8字符串部分字符以负数表示?
为何C++中部分字符以负数表示?
测试源码
#include <cstring> #include <iostream> int main() { char str[] = "Hello, привет, 😎!"; std::cout << str << "\n\n"; for (int i = 0; i < std::strlen(str); i++) { std::cout << (int) str[i] << ' '; } std::cout << "\n\n"; for (int i = 0; i < std::strlen(str); i++) { std::cout << std::hex << (int) str[i] << ' '; } std::cout << "\n\n"; for (int i = 0; i < std::strlen(str); i++) { std::cout << std::hex << (str[i] & 0xff) << ' '; } std::cout << '\n'; return 0; }
编译与运行环境
- 源码保存为无BOM的UTF-8编码文件
chars.cpp - Windows 10:使用MSVC编译器(cl.exe),命令:
cl /EHsc /utf-8 "chars.cpp",运行前需在cmd.exe执行chcp 65001配置代码页 - Ubuntu(WSL 2):使用g++编译器,命令:
g++ /mnt/c/Users/Илья/source/repos/test/chars.cpp -o chars
运行结果
Hello, привет, 😎! 72 101 108 108 111 44 32 -48 -65 -47 -128 -48 -72 -48 -78 -48 -75 -47 -126 44 32 -16 -97 -104 -114 33 48 65 6c 6c 6f 2c 20 ffffffd0 ffffffbf ffffffd1 ffffff80 ffffffd0 ffffffb8 ffffffd0 ffffffb2 ffffffd0 ffffffb5 ffffffd1 ffffff82 2c 20 fffffff0 ffffff9f ffffff98 ffffff8e 21 48 65 6c 6c 6f 2c 20 d0 bf d1 80 d0 b8 d0 b2 d0 b5 d1 82 2c 20 f0 9f 98 8e 21
问题解答
出现负数的核心原因是**char类型的有符号性由编译器/平台决定**,C++标准并未强制规定char是有符号还是无符号类型:
- 在x86/x64架构的Windows和Ubuntu(WSL 2)上,默认
char是有符号类型。 - UTF-8编码中,非ASCII字符(比如俄语"привет"、表情😎)的每个字节最高位都是1。当这些字节存储到有符号
char中时,最高位的1会被当作符号位,因此数值会被解释为负数。 - 当把有符号
char强制转换为int时,会发生符号扩展:原char的符号位会填充到int的高位,所以十六进制输出会出现一串ffff前缀(比如0xd0作为有符号char会被扩展为0xffffffd0)。 - 第三个循环中
str[i] & 0xff的作用是将char当作无符号值处理:0xff是8位全1的掩码,与操作会清除符号扩展带来的高位1,得到该字节的原始无符号十六进制值。
这种行为属于实现特定行为,而非C++标准强制规定。如果需要统一的无符号字节处理,可以显式使用unsigned char类型来存储字符串。
内容的提问来源于stack exchange,提问作者Ilya Chalov
相关产品推荐
相关产品推荐

