我有一小段代码:
#include <locale.h>
#include <stdlib.h>
#include <stdio.h>
#include <string>
wchar_t widec('€');
wchar_t widecl(L'€');
std::string tc("€");
int main(int argc, char *argv[])
{
printf("printf as hex - std::string tc(\"€\") = %x %x %x\n\r", tc.c_str()[0], tc.c_str()[1], tc.c_str()[2]);
printf("printf as hex - wchar_t widec('€') = %x\n\r", widec);
printf("printf as hex - wchar_t widecl(L'€') = %x\n\r", widecl);
return 0;
}
输出:
printf as hex - std::string tc("€") = ffffffe2 ffffff82 ffffffac
printf as hex - wchar_t widec('€') = e282ac
printf as hex - wchar_t widecl(L'€') = 20ac
我不明白两件事。
为什么tc.c_str()
(其[0]
,[1]
和[2]
索引准确无误)打印为UTF-8,看起来像UTF-16/32领先FF字节?
为什么初始化相同的wchar_t
变量会产生不同的输出,具体取决于是否使用L
前缀,即。使用它似乎产生UTF-16/32内容和没有L
前缀的UTF-8,为什么会这样?
答案 0 :(得分:1)
没有显式符号说明符的char
是signed
或unsigned
,具体取决于编译器。该标准没有规定默认类型,它是编译器供应商的选择。
将char
传递给print()
会将值从8位扩展到32位。然后%x
打印该32位值的位,默认忽略前导零(除非您在%x
上使用长度说明符来保留它们)。 8位值如何扩展到32位取决于它的实际类型。
在您的情况下,您看到的额外f
是由于char
值为符号扩展。 0xEx
,0x8x
和0xAx
的高位均为1,因此1用于在扩展期间填充高24位。这意味着您的编译器将char
实现为signed
类型,并将值扩展为signed int
。您可以手动将char
值类型转换为unsigned
,以强制它们零扩展:
printf("printf as hex - std::string tc(\"€\") = %x %x %x\n",
(unsigned char) tc[0], (unsigned char) tc[1], (unsigned char) tc[2]);
(请注意,我删除了c_str()
的使用,在您的示例中没有必要)
'€'
和"€"
没有任何前缀的解释取决于源文件保存的编码,以及编译器配置为运行的编码。
无前缀'€'
和"€"
文字可以是UTF-8的唯一方法是,如果您的源代码文件以UTF-8保存(强制使用UTF-8文字,您可以在C ++ 11及更高版本中使用u8
前缀。以不同的编码保存文件,您将看到不同的结果。然后,该解释的结果按原样分配给tc
,并按wchar_t
编码为widec
。
另一方面,L
前缀迫使编译器将L'€'
解释为宽文字而不是狭义文字,因此不应该如何解释它。它知道文字是Unicode,因此它确定了Unicode代码点值,然后将其编码为wchar_t
值(wchar_t
在Windows上为16位,在其他平台上为32位)在{ {1}}。 widecl
的Unicode代码点为U+20AC EURO SIGN
。