提问人:Ilya Chalov 提问时间:4/29/2023 更新时间:4/29/2023 访问量:163
为什么在 C++ 中,多字节 UTF-8 字符串中的某些字符由负数表示?
Why in C++ are some characters in a multibyte UTF-8 string represented by negative numbers?
问:
以下 C++ 源代码我在“Windows 10”和“Ubuntu”(通过“WSL 2”)中编译和运行:
#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;
}
我将此源代码保存在 UTF-8 编码的文件中,无需 BOM。在 Windows 10 中,我从命令行使用“Microsoft C++ Build Tools”中的 MSVC 编译器 (cl.exe)。在“Ubuntu”(通过“WSL 2”)中,我使用来自命令行设置的“GCC”的 g++ 编译器。chars.cpp
cl /EHsc /utf-8 "chars.cpp"
g++ /mnt/c/Users/Илья/source/repos/test/chars.cpp -o chars
我得到了以下结果(在“Windows 10”中,您需要使用命令在控制台中配置代码页,在“Ubuntu”(通过“WSL 2”)中,这不是必需的):cmd.exe
chcp 65001
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
我很好奇为什么用负数来表示某些字符。我试图在 cppreference.com 中找到解释,并在那里阅读了两篇文章:
https://en.cppreference.com/w/cpp/language/types,引用:
char - 字符表示的类型,可以在目标系统上最有效地处理(具有与有符号字符或无符号字符相同的表示和对齐方式,但始终是不同的类型)。多字节字符字符串使用此类型来表示代码单元。对于范围 [0, 255] 中 unsigned char 类型的每个值,将该值转换为 char,然后再转换回 unsigned char 将生成原始值。(从 C++ 11 开始)char 的有符号取决于编译器和目标平台:ARM 和 PowerPC 的默认值通常为无符号,x86 和 x64 的默认值通常为有符号。
和
https://en.cppreference.com/w/cpp/string/multibyte
但我在那里没有找到直接的解释。
我的问题。某些字符用负数表示的目的是什么?它是在标准中还是在特定于系统的情况下?
答:
感谢人们的评论,我想我明白这里发生了什么。如果我错了,请纠正我。
据我了解,C++ 语言标准允许编译器解释为 or .char
signed char
unsigned char
默认情况下,MSVC 和 g++ 编译器会解释为。因此,我的程序中的类型可以表示 范围内的值。考虑西里尔文小写字母的例子:在Unicode表中是;在 UTF-8 编码中,这是 2 个字节(十六进制)或 (dec)。char
signed char
char
-128..127
'п'
U+043F
d0 bf
208 191
由于这些数字不适合这个范围,它们被转换为(208 - 256, 191 - 256)。这就是所有字符的处理方式。事实证明,如果字符代码落入 ,则它不会改变(ASCII表)。208 191
-128..127
-48 -65
0..127
可以使用特殊开关(选项)更改 MSVC 和 g++ 编译器的此行为。MSVC 编译器有一个选项:/J
cl /EHsc /utf-8 /J "chars.cpp"
对于 g++ 编译器,有一个选项:-funsigned-char
g++ /mnt/c/Users/Илья/source/repos/test/chars.cpp -o chars -funsigned-char
因此,使用新选项编译和运行相同的源代码后将给出不同的结果:
Hello, привет, 😎!
72 101 108 108 111 44 32 208 191 209 128 208 184 208 178 208 181 209 130 44 32 240 159 152 142 33
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
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
使用新选项,编译器将解释为 (range ),因此字符串表示中没有负数。char
unsigned char
0..255
评论
char
signed char
unsigned char
char
评论
char
在编译器中。signed char
char
char
char
signed char
unsigned char
char
signed char
char8_t
unsigned char
std::u8string
char16_t
std::u16string
char32_t
std::u32string