为什么C11或C ++ 11中没有ASCII或UTF-8字符文字?

时间:2012-06-07 19:08:39

标签: c++ utf-8 c++11 ascii c11

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

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

编辑:

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

5 个答案:

答案 0 :(得分:9)

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

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

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

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

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

如果您使用的是C11,则可以在字符常量上使用uU前缀,但不能使用u8前缀...

/* 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系统(example)。这需要大量的移植工作,除非您知道要在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兼容性。

答案 1 :(得分:8)

UTF-8字符文字必须有可变长度 - 对于很多大多数,不可能在charwchar中存储单个字符,是什么类型它应该有吗?由于我们在C语言和C ++中没有可变长度类型,除了固定大小类型的数组之外,唯一合理的类型是const char * - 并且C字符串需要以空终止,所以它不会改变任何东西。

至于编辑:

引用C ++ 11标准:

  

基本源字符集成员的字形旨在识别ISO / IEC 10646子集中与ASCII字符集对应的字符。但是,由于源文件字符到源字符集的映射(在转换阶段1中描述)被指定为实现定义,因此需要一个实现来记录源文件中基本源字符的表示方式。

(2.3.1的脚注)。

我认为这是不保证它的充分理由。虽然,正如您在此处的评论中所指出的,对于大多数(或每个)主流编译器,字符文字的ASCII-ness是实现保证。

答案 2 :(得分:7)

对于C ++,Evolution Working Group issue 119: Adding u8 character literals Motivation 部分说明了这一点:

  

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

     

EWG讨论了在Rapperswil中添加u8字符文字的想法,并接受了这一改变。本文为此提供了措辞   扩展

使用N4267: Adding u8 character literals中的措辞将其纳入工作草案中,我们可以在此时找到最新的标准草案N4527中的措辞,并在2.14.3部分说明它们是有限的编码适合单个UTF-8代码单元的点:

  

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

答案 3 :(得分:0)

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

答案 4 :(得分:0)

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

关于'"0",您正在阅读,只有后者保证与u8"0"相同,即使在EBCDIC系统上也是如此。顺便说一句,如果您注意{ 0x30, 0 }预定义标识符,那么前者不能在您的代码中方便地处理。