17

为什么即使有 UTF-8 字符串文字,C11 或 C++11 中也没有 UTF-8 字符文字?我知道,一般来说,字符文字表示单个 ASCII 字符,它与单个八位字节的 UTF-8 代码点相同,但是 C 和 C++ 都没有说编码必须是 ASCII。

基本上,如果我阅读标准正确,则不能保证'0'将表示整数 0x30,但u8"0"必须表示字符序列 0x30 0x00。

编辑:

我知道不是每个 UTF-8 代码点都适合一个字符。这样的文字仅对单八位字节代码点(又名 ASCII)有用,所以我想称它为“ASCII 字符文字”会更合适,所以问题仍然存在。我只是选择用 UTF-8 来构建问题,因为有 UTF-8 字符串文字。我可以想象可移植地保证 ASCII 值的唯一方法是为每个字符编写一个常量,考虑到只有 128 个字符,这不会那么糟糕,但仍然......

4

5 回答 5

10

编写不可移植的 C 代码是完全可以接受的,这是这样做的众多充分理由之一。随意假设您的系统使用 ASCII 或其某些超集,并警告您的用户不要尝试在 EBCDIC 系统上运行您的程序。

如果您感觉非常慷慨,您可以对支票进行编码。众所周知,该gperf程序会生成包含此类检查的代码。

_Static_assert('0' == 48, "must be ASCII-compatible");

或者,对于 C11 之前的编译器,

extern int must_be_ascii_compatible['0' == 48 ? 1 : -1];

如果您在 C11 上,则可以在字符常量上使用uor前缀,但不能使用前缀...Uu8

/* This is useless, doesn't do what you want... */
_Static_assert(0, "this code is broken everywhere");
if (c == '々') ...

/* This works as long as wchar_t is UTF-16 or UTF-32 or UCS-2... */
/* Note: you shouldn't be using wchar_t, though... */
_Static_assert(__STDC_ISO_10646__, "wchar_t must be some form of Unicode");
if (c == L'々') ...

/* This works as long as char16_t is UTF-16 or UCS-2... */
_Static_assert(__STDC_UTF_16__, "char16_t must be UTF-16");
if (c == u'々') ...

/* This works as long as char32_t is UTF-32... */
_Static_assert(__STDC_UTF_32__, "char32_t must be UTF-32");
if (c == U'々') ...

一些项目是用非常可移植的 C 语言编写的,并且已经移植到非 ASCII 系统(示例)。这需要大量的移植工作,除非您知道要在 EBCDIC 系统上运行您的代码,否则没有真正的理由去做这些工作。

关于标准:编写 C 标准的人必须应对所有可能的 C 实现,包括一些非常奇怪的实现。有已知的系统,其中sizeof(char) == sizeof(long), CHAR_BIT != 8, 整数类型具有陷阱表示,sizeof(void *) != sizeof(int *), sizeof(void *) != sizeof(void (*)()),va_list是堆分配的,等等。这是一场噩梦。

不要因为尝试编写将在您从未听说过的系统上运行的代码而自责,也不要努力寻找 C 标准中的保证。

例如,就 C 标准而言,以下是 的有效实现malloc

void *malloc(void) { return NULL; }

请注意,虽然u8"..."常量保证为 UTF-8,u"..."并且U"..."除了编码分别为每个字符 16 位和 32 位之外没有任何保证,并且实际编码必须由实现记录。

摘要:在 2012 年可以安全地假设 ASCII 兼容性。

于 2012-06-07T20:07:51.727 回答
8

UTF-8 字符文字必须具有可变长度 - 对于大多数来说,不可能将单个字符存储在charorwchar中,那么它应该具有什么类型呢?由于我们在 C 和 C++ 中没有可变长度类型,除了固定大小类型的数组,唯一合理的类型是const char *- 并且 C 字符串需要以空值结尾,所以它不会改变任何事物。

至于编辑:

引用 C++11 标准:

基本源字符集成员的字形旨在识别对应于 ASCII 字符集的 ISO/IEC 10646 子集中的字符。然而,因为从源文件字符到源字符集的映射(在翻译阶段 1 中描述)被指定为实现定义,所以需要一个实现来记录基本源字符在源文件中是如何表示的。

(2.3.1 的脚注)。

我认为这是不保证的充分理由。尽管,正如您在此处的评论中指出的那样,对于大多数(或每个)主流编译器,字符文字的 ASCII 特性是有实现保证的。

于 2012-06-07T19:11:15.147 回答
6

对于 C++,进化工作组第 119 期已经解决了这个问题:添加 u8 字符文字,其动机部分说:

我们有五个用于字符串文字的编码前缀(无、L、u8、u、U),但只有四个用于字符文字——缺少的一个是 u8。如果窄执行字符集不是 ASCII,则 u8 字符文字将提供一种方法来编写具有保证 ASCII 编码的字符文字(单代码单元 u8 编码正是 ASCII)。添加对这些文字的支持将添加一个有用的功能并使语言更加一致。

EWG 讨论了在 Rapperswil 中添加 u8 字符文字的想法并接受了更改。本文提供了该扩展的措辞。

这已使用 N4267 中的措辞合并到工作草案中:添加 u8 字符文字,我们可以在此时最新的草案标准N4527中找到措辞,并注意部分2.14.3说它们仅限于适合单个 UTF-8 的代码点代码单元:

以 u8 开头的字符文字,例如 u8'w',是 char 类型的字符文字,称为 UTF-8 字符文字。UTF-8 字符文字的值等于其 ISO10646 代码点值,前提是代码点值可以用单个 UTF-8 代码单元表示(即,假设它是一个 US-ASCII 字符)。包含多个 c 字符的 UTF-8 字符文字格式不正确。

于 2015-06-16T16:01:50.413 回答
0

如果您不相信您的编译器会将'0'其视为 ASCII 字符 0x30,那么您可以static_cast<char>(0x30)改用它。

于 2012-06-07T19:33:00.507 回答
0

如您所知,UTF-8 编码的字符需要几个八位字节,因此需要chars,因此它们的自然类型是char[],这确实是带有u8-prefixed 字符串文字的类型!所以 C11 在这里是正确的,只是它坚持使用"for a string的语法约定,需要用作 char 数组,而不是使用隐含的基于语义的建议'

关于"0"vs u8"0",您没看错,只有后者保证与 相同{ 0x30, 0 },即使在 EBCDIC 系统上也是如此。顺便说一句,如果您注意__STDC_MB_MIGHT_NEQ_WC__预定义的标识符,则可以在您的代码中方便地处理前者。

于 2013-11-01T17:03:08.410 回答