将 UTF8 字符文字分配给 char16_t - 字符常量中的字符太多

Assigning UTF8 char literal to char16_t - too many chars in char constant

我正在为嵌入式系统创建 UTF8 table 查找。 table用于将UTF8编码的字符转换为字体(数组)中的位图索引。

我收到警告“多字符字符文字(潜在的可移植性问题)”。 “conversion_table”数组中的每个条目都带有此警告标记。

代码如下:

typedef struct UTF8_To_Bitmap_Index_s
{
    char16_t    encoded_character;
    uint8_t     bitmap_index;
} UTF8_To_Bitmap_Index_t;

size_t width_wchar_t = sizeof(wchar_t);

UTF8_To_Bitmap_Index_t conversion_table[] =
{
    {'¡', 0x00},
    {'À', 0x00},
    {'Á', 0x00},
    {'Ã', 0x00},
    {'Ä', 0x00},
    {'Å', 0x00},
    {'Ç', 0x00},
    {'É', 0x00},
    {'Í', 0x00},
    {'Ó', 0x00},
    {'Õ', 0x00},
    {'Ö', 0x00},
    {'Ø', 0x00},
    {'Ú', 0x00},
    {'Ü', 0x00},
    {'ß', 0x00},
    {'à', 0x00},
    {'á', 0x00},
    {'â', 0x00},
    {'ã', 0x00},
    {'ä', 0x00},
    {'å', 0x00},
    {'æ', 0x00},
    {'ç', 0x00},
    {'è', 0x00},
    {'é', 0x00},
    {'ê', 0x00},
    {'í', 0x00},
    {'ñ', 0x00},
    {'ó', 0x00},
    {'ô', 0x00},
};

有什么方法可以修改上面的代码来消除警告吗?
(注意:在确定实际位图索引之前,0x00 是一个占位符。)

生成的数据正确:

     50          UTF8_To_Bitmap_Index_t conversion_table[] =
   \                     conversion_table:
   \   00000000   0xC2A1             DC16 49825
   \   00000002   0x00 0x00          DC8 0, 0
   \   00000004   0xC380             DC16 50048
   \   00000006   0x00 0x00          DC8 0, 0
   \   00000008   0xC381             DC16 50049
   \   0000000A   0x00 0x00          DC8 0, 0
   \   0000000C   0xC383             DC16 50051
   \   0000000E   0x00 0x00          DC8 0, 0
   \   00000010   0xC384             DC16 50052
   \   00000012   0x00 0x00          DC8 0, 0
   \   00000014   0xC385             DC16 50053
   \   00000016   0x00 0x00          DC8 0, 0
   \   00000018   0xC387             DC16 50055
   \   0000001A   0x00 0x00          DC8 0, 0
   \   0000001C   0xC389             DC16 50057
   \   0000001E   0x00 0x00          DC8 0, 0
   \   00000020   0xC38D             DC16 50061
   \   00000022   0x00 0x00          DC8 0, 0
   \   00000024   0xC393             DC16 50067
   \   00000026   0x00 0x00          DC8 0, 0
   \   00000028   0xC395             DC16 50069
   \   0000002A   0x00 0x00          DC8 0, 0
   \   0000002C   0xC396             DC16 50070
   \   0000002E   0x00 0x00          DC8 0, 0
   \   00000030   0xC398             DC16 50072
   \   00000032   0x00 0x00          DC8 0, 0
   \   00000034   0xC39A             DC16 50074
   \   00000036   0x00 0x00          DC8 0, 0
   \   00000038   0xC39C             DC16 50076
   \   0000003A   0x00 0x00          DC8 0, 0
   \   0000003C   0xC39F             DC16 50079
   \   0000003E   0x00 0x00          DC8 0, 0
   \   00000040   0xC3A0             DC16 50080
   \   00000042   0x00 0x00          DC8 0, 0
   \   00000044   0xC3A1             DC16 50081
   \   00000046   0x00 0x00          DC8 0, 0
   \   00000048   0xC3A2             DC16 50082
   \   0000004A   0x00 0x00          DC8 0, 0
   \   0000004C   0xC3A3             DC16 50083
   \   0000004E   0x00 0x00          DC8 0, 0
   \   00000050   0xC3A4             DC16 50084
   \   00000052   0x00 0x00          DC8 0, 0
   \   00000054   0xC3A5             DC16 50085
   \   00000056   0x00 0x00          DC8 0, 0
   \   00000058   0xC3A6             DC16 50086
   \   0000005A   0x00 0x00          DC8 0, 0
   \   0000005C   0xC3A7             DC16 50087
   \   0000005E   0x00 0x00          DC8 0, 0
   \   00000060   0xC3A8             DC16 50088
   \   00000062   0x00 0x00          DC8 0, 0
   \   00000064   0xC3A9             DC16 50089
   \   00000066   0x00 0x00          DC8 0, 0
   \   00000068   0xC3AA             DC16 50090
   \   0000006A   0x00 0x00          DC8 0, 0
   \   0000006C   0xC3AD             DC16 50093
   \   0000006E   0x00 0x00          DC8 0, 0
   \   00000070   0xC3B1             DC16 50097
   \   00000072   0x00 0x00          DC8 0, 0
   \   00000074   0xC3B3             DC16 50099
   \   00000076   0x00 0x00          DC8 0, 0
   \   00000078   0xC3B4             DC16 50100
   \   0000007A   0x00 0x00          DC8 0, 0

资源:
编译器——IAR 嵌入式 Workbench 版本 7.4
目标平台:ARM Cortex M

根据标准(§6.4.4.4.2 和 §6.4.4.4.10),代码原样不可移植:

An integer character constant is a sequence of one or more multibyte characters enclosed in single-quotes, as in 'x'. A wide character constant is the same, except prefixed by the letter L, u, or U. ... The value of an integer character constant containing more than one character (e.g., 'ab'), […] is implementation-defined. ...


您正在将字符编码为 char16_t,而且根据标准,您不应使用 ' ' 语法,而应使用 u' ' 语法:

这应该可以解决您的问题:

UTF8_To_Bitmap_Index_t conversion_table[] =
{
    {u'¡', 0x00},
    {u'À', 0x00},
    ...

尝试将 UTF-8 编码的字节序列存储在 char16_t 中基本上是不正确的,即使它适合(并且通常不能保证这一点,因为 UTF-8 代码序列可以是从一到四个字节长)。 char16_t 的预期目的是存储单个 UTF-16 代码值(不一定是整个字符,但这是另一回事)。 [注1]

当然,16 位就是 16 位,所以如果您真的愿意,可以将两个八位字节混成一个 char16_t。但是不要指望编译器会在没有警告的情况下接受它。

如果您绝对知道 UTF-8 序列是两个字节长,那么您应该将它存储在 char[2] 中。如果您希望能够将这两个字符称为标量,则可以将 char[2]char16_t 输入双关语,但严格的别名规则可能会妨碍您。此外,您需要仔细考虑您目前刚刚忽略的字节顺序问题。

当您从串行端口(或 UTF-8 编码的文件或套接字,或其他任何东西)接收到 UTF-8 编码的序列时,您将首先接收到第一个字节,这是理所当然的。如果将这些字符中的两个映射到一个双字节整数,则整数的低地址字节将包含第一个字节,整数的高地址字节将包含第二个字节。如果您使用高位字节具有低位地址的大端架构,那就太完美了。也许您正在大端环境中工作。但如果没有,您可能会发现您的输入与您创建的常量不匹配。

如您看到的警告所示,没有标准方法可以将双字节序列转换为整数(请记住,在 C 中,字符文字是 int,而不是char)。因此,给定的编译器可能会做任何事情,包括将字符文字限制为单个字节,但编译器通常将多个字符编码为 base-256 数字。因此,'AB'\x4142 都产生整数 0x4142。但是如果你要将该整数映射到小端机器上的 char[4],你将看到的是字节序列 0x42 0x41 0x00 0x00,如果你将它打印到控制台,它将显示为 BA.

根据您如何生成用于查找的双字节密钥 table,这可能会或可能不会给您想要的结果。无论如何,它不会成为 portable(甚至是面向未来的),因为没有标准机制可以从两字节的 UTF-8 编码中创建 16 位编译时整数。

不过,这个谜题还有一部分。您的程序似乎包含以下内容:

    {'ß', 0x00},

但我们知道(即使为了简单起见我们宁愿忽略这一事实)计算机内部不存在字符之类的东西。您会发现所有这些都是 0 和 1。如果我们真的很准确,你也不会找到那些,因为在串行总线内没有从一个电极到另一个电极的微观零点。相反,有些亚原子现象可以被视为适合两种不同的状态。但是我们不需要下降到那种物理描述水平;可以说保存程序的文件不包含小字符而是包含位序列就足够了。问题是,究竟有哪些比特序列?特别是,哪些(以及多少)位显示为 ß?答案由文件的字符编码定义。

我的猜测是您使用使用 UTF-8 编码的编辑器编写了该源文件,因此 ß 显示为两个字节序列 C3 9F。现在,当编译器看到这两个字节时会发生什么?

C 标准不需要任何特定的编码,但它允许编译器将其输入视为单字节字符序列,每个字符代表基本源字符集中的字符,不包括ß。编译器对于如何处理任何与源字符集中的字符不对应的字节,以及如何将这些字节映射到 executable 中的字符和字符串(这是允许的使用与源文件不同的编码。)这一切都变得有点复杂;也许我稍后会添加完整的解释。可以这么说,许多编译器只是将一个字节视为一个字节,至少在字符和字符串文字中是这样;字节只是通过而不考虑编码。 (其他编译器使用更复杂的算法,考虑到源代码和执行编码,这可能会有所不同。但在简单的情况下,结果是相同的。)

所以这就是编译器抱怨 'ß' 不止一个字符的原因:它是,因为它被编码为两个字节。 (如果您使用 Latin-1 作为源字符集和执行字符集,那么 ß 将只是一个字节,0xDF,并且编译器不会抱怨。但这不会让您得到一个 UTF- 8转换table.)

C11(和当代的C++版本)特权Unicode和UTF-8传输编码,这是完全合适的。它通过提供一种允许您使用基本源字符集明确指定 Unicode 字符代码的语法,并通过提供描述所需编码的字符串和字符文字前缀来解决多个区域设置的一些混乱。如果您有这样的编译器,您可以将 ß 写成 \u00DF,这是它的 Unicode 代码点,并使用 u8 前缀将其包含在 UTF-8 字符串文字中:u8"\u00DF". [注2]

备注

  1. 从技术上讲,如果预处理器宏 __STDC_UTF_16__uchar.h 中定义,则 char16_t 仅使用 UTF-16 标识,对于 char32_t 也是如此和 __STDC_UTF_32__。但我仍然认为可以公平地说,预期用途是 Unicode 编码。

  2. 如果您想使用 UTF-16 或 UTF-32 编码,您可以通过编写 u"\u00DF"char32_t[] 来创建 char16_t[] 字符串文字字符串文字,U"\u00DF"。这两个都有两个元素,包括 NUL 终止符。 (其中一个可能与宽字符字符串文字 L"\u00DF" 相同,但这取决于配置的执行区域设置和编译器支持。)您还可以使用 char16_tchar32_t字符文字。但请注意 u'\u00DF' 的值为 0xDF,这是 ß 的 Unicode 代码点。