1

我正在为嵌入式系统创建一个 UTF8 表查找。该表用于将 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 Embedded Workbench 7.4 版
目标平台:ARM Cortex M

4

2 回答 2

3

尝试将 UTF-8 编码的字节序列存储在 a 中基本上是不正确的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 中,字符文字是 an int,而不是 a char)。因此,给定的编译器可能会做任何事情,包括将字符文字限制为单个字节,但编译器通常会将多个字符编码为 base-256 数字。因此,'AB'两者\x4142都产生整数0x4142。但是,如果您将该整数映射到char[4]小端机器上的 a 上,您将看到的是字节序列0x42 0x41 0x00 0x00,如果您将其打印到控制台,它将显示为BA.

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

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

    {'ß', 0x00},

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

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

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

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

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

笔记

  1. 从技术上讲,char16_t仅当预处理器宏__STDC_UTF_16__在 中定义时才使用 UTF-16 标识,对于和uchar.h也是类似的。但我仍然认为可以公平地说预期用途是 Unicode 编码。char32_t__STDC_UTF_32__

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

于 2021-09-01T23:41:27.510 回答
2

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

整数字符常量是包含在单引号中的一个或多个多字节字符的序列,如 'x'。宽字符常量是相同的,除了以字母 L、u 或 U 为前缀。...包含多个字符(例如,'ab')的整数字符常量的值,[…] 是实现定义的. ...


您将字符编码为char16_t,并且按照标准,您不应使用' '语法,而应使用u' '语法:

在此处输入图像描述

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

UTF8_To_Bitmap_Index_t conversion_table[] =
{
    {u'¡', 0x00},
    {u'À', 0x00},
    ...
于 2021-09-01T18:13:24.193 回答