什么是“有效的”C标识符?

xzlaal3s  于 2023-05-06  发布在  其他
关注(0)|答案(4)|浏览(147)

在@Zaibis的建议(和我自己的 * 答案 * What are the valid characters for macro names?,以及😃 (and other Unicode characters) in identifiers not allowed by g++))...
clang允许很多“疯狂”的角色。虽然我一直在努力寻找很多押韵或理由-为什么有些是允许的(🔴½),而其他人则不是(▶ ★ ©)。
例如,下面的代码都编译为A-OK(clang-700.1.76

#define 💩  ?:          // OK (Pile of poo)
#define  ■  @end        // OK (HALFWIDTH BLACK SQUARE)
#define 🅺  @interface  // OK (NEGATIVE SQUARED LATIN CAPITAL LETTER K)
#define P  @protocol   // OK (FULLWIDTH LATIN CAPITAL LETTER P)

然而,以下所有代码都会导致相同的编译器错误……
Macro name must be an identifier.

#define  ☎     TEL
#define ❌     NO
#define  ⇧     UP
#define  〓    ==
#define  🍎   APPLE

Clang的文档提到了这个问题,只说明了...
支持C99和C++中的扩展标识符。此功能允许标识符包含活动语言标准指定的某些Unicode字符;这些字符可以使用UTF-8编码直接写入源文件,或者使用通用字符名(\u00E0,\U000000E0)引用。
所以,我想我是在问..什么是“活动语言标准”,以及如何找到哪些标识符法律的的权威来源。
我创建following code只是为了看看Clang会用它做什么。在测试的大约63488个可能的标识符中,有23个发出警告,9506个生成错误。这就留下了将近54,000个有效字符用于标识符。当然可以,但是谁被砍了呢?为什么?

osh3o9ms

osh3o9ms1#

正如其他人所提到的,ISO/IEC 9899:2011的附录D列出了C11中通用字符名有效字符的十六进制值。我一直在寻找一个答案,“为什么”这个名单被选中。

字符集标准

首先,有两个相关的标准定义一组字符:ISO/IEC 10646(定义UCS)和Unicode。为了进一步混淆(或简化)事情,它们都定义了相同的字符,因为ISO和Unicode将它们保持为synchronized。UCS本质上只是一个将值与一组字符相关联的字符Map表(“repertoire”),而Unicode还提供了进一步的定义,例如如何按字母排序顺序比较字符串(collation),哪些代码点表示“规范等效”字符(normalization),以及如何处理从右向左书写的语言中的字符的bidirectional algorithm等等。

C中的通用字符名

通用字符名(UCN)是C99(ISO/IEC 9899:1999)中新增加的一项功能。在“国际标准的基本原理---编程语言---C”(Rev. 2, Oct. 1999)中,其目的是“允许在标识符、字符串和字符常量中使用任何'本机'字符,同时保留C的可移植性目标”(第2节)。本节继续讨论如何在C中编码这些字符(\U\u形式与多字节字符或本机编码)以及如何处理它的策略模型(第14页,参见PDF第22页)。

基本原理

我希望同样的"rationale" document from 1999能给予为什么每个扩展字符范围被选择为C99的UCN可接受的原因。理由陈述的附件I全文如下:

附件一标识符通用字符名称(规范性)

A new feature of C9X.
这不是一个很大的理由。他们甚至不知道C标准会在哪一年发布,所以它就被称为“C9 X”。后来的rationale document from 2003稍微更有启发性:

附录D标识符通用字符名称(规范性)

  • C99的新功能。*

目的是与ISO/IEC TR 10176保持一致。
ISO/IEC TR 10176是“编程语言标准编写指南”。它基本上是编写编程语言标准的人的指南。它包括在编程语言中使用字符集的指导方针以及“推荐的用户定义标识符扩展指令集”(附录A)。但是,这段引用自2003年基本原理文件的话只是“保持最新的意图”,而不是严格遵守TR 10176的承诺。
ISO/IEC TR 10176:2003 table是一个公开可用的字符。字符值参考ISO 10646。该表将来自多种语言的字符范围分类为“大写”Lu;“小写”Ll;“数字、十进制数字”Nd,“标点符号、连接符”Pc;应该清楚这样的分类对编程语言的用途。
需要注意的是,TR 10176是一份技术报告,而不是标准。我在论坛和其他编程语言(如Ada、COBOL和D语言)相关的文档中发现了几个对它的引用。大部分的讨论都是关于这些语言的标准应该遵循TR 10176(不是标准)的程度,以及TR 10176落后于ISO 10646更新的抱怨。
也许最具启发性的是文档WG21/N3146:“C和C++扩展标识符字符的建议”。它以2010年向标准机构提出的建议对标识符的初始字符进行限制的评论开始。它提到了类似的抱怨C引用TR 10176,并根据Unicode的标识符和模式语法和XML的Common Syntactic Constructs的限制,建议哪些字符应该被允许作为标识符的初始字符。WG 21/N3146给出了后来出现在C11标准ISO/IEC 9899:2011中的建议措辞。文档末尾有一个表,有助于说明所选的字符范围。

C11允许和不允许字符

下面是 * 扩展 * 标识符字符范围的编译列表。黑体范围是C11(ISO/IEC 9899:2011 Annex D)中给出的范围。对C11* 中未列出的 * 斜体 * 范围 * 添加了一些注解(即不允许)。它们要么在WG21/N3146中被Unicode的UAX#31或XML的公共语法构造标记为不允许,要么被其他注解禁止。

00A8、00AA、00AD、00AF、00B2-00B5、00C0-00D6、00D8-00F6、00F8-00FF:(各种字符,如阴性ª和阳性º序数指示符、带变音符号的元音、数字字符(如上标数字、分数等))

  • (以前的差距)*:UAX 31和/或XML禁止所有这些操作。(一般标点符号如«»、货币符号¥£、数学运算符×÷等)
    0100-167F:(拉丁语、希腊语、西里尔语、阿拉伯语、泰语、埃塞俄比亚语等---还有很多其他语言)
  • 1680*:“Ogham块包含脚本特定的空格:“ 的情况

1681-180D:(欧甘语、他加禄语、蒙古语等)

  • 180 E *:“蒙古语块包含脚本特定空格”
    180F-1FFF:(更多语言…语音学、扩展拉丁语和希腊语等)
  • 2000*:启动“常规标点”块,但允许某些标点:
    200B−200D、202A−202E、203F−2040、2054、2060−206F:(选自“常规标点符号”框)
    2070−218F:上标和下标,货币符号,符号的组合变音符号,字母符号,数字形式
  • 2190-245F*:箭头、数学运算符、其他技术、控制图片、光学字符识别
    2460-24FF:“封闭字母数字”
  • 2500*:开始“框绘制、块元素、几何形状”等。
    2776-2793:(一些dingbats和圈出的dingbats)
  • 2794-2BFF*:(不同的丁巴特集,数学符号,箭头,盲文图案等)
    2C00-2DFF、2E80-2FFF:“格拉哥里,拉丁扩展-C,科普特,格鲁吉亚补充,Tifinagh,埃塞俄比亚扩展,西里尔扩展-A”(也CJK字根补充)
  • 3000*:(“CJK符号和标点符号”的开头,允许某些选择)
    3004-3007、3021-302F、3031-303F:(允许“CJK符号和标点符号”)
    3040-D7FF:平假名、片假名、更多中日韩表意文字、部首等
  • D800-F8FF*:(这将启动High和低替代区域(编码所需的数字空间)以及Private Use
    F900-FD3D、FD40-FDCF、FDF0-FE44、FE47-FFFD:选自“CJK兼容表意文字”、“阿拉伯文表示形式”等。10000−1FFFD、20000−2FFFD、30000−3FFFD、40000−4FFFD、50000−5FFFD、60000−6FFFD、70000−7FFFD、80000−8FFFD、90000−9FFFD、A0000− AFFD、B0000−BFFFD、C0000−CFFFD、D0000− DFFD、E0000−EFFFDWG21/N3146给出了这些最终范围的依据:

补充私人使用区从F0000延伸到10 FFFF;[AltId]和[XML 2008]都不允许该范围内的字符。
此外,[AltId]不允许每个平面的最后两个代码位置作为非字符,即P FFFE或P FFFF形式的每个位置,对于P的任何值。
C11附录D.2中的“最初不允许的字符范围”为0300− 036 F、1DC 0 −1DFF、20 D 0 − 20 FF、FE 20 − FE 2F
将这个WG21/N3146放在C11 standard的附件D旁边,可以推断出它们是如何排列的。例如,数学运算符和标点符号似乎是不允许的。我希望这对“为什么”或“如何”选择允许的字符有一些启发。

TLDR;版本

gdx19jrr

gdx19jrr2#

C 2011 standard

6.4.2标识符

6.4.2.1概述

...
3 标识符中的每个通用字符名称应指定其在ISO/IEC 10646中的编码福尔斯D.1.71中规定的范围之一的字符。)首字符不应是指定其编码落入D.2中规定的范围之一的字符的通用字符名称。一种实现可以允许不是基本源字符集的一部分的多字节字符出现在标识符中;哪些字符以及它们与通用字符名的对应关系是实现定义的。
... 71)在连接器不能接受扩展字符的系统中,可以使用通用字符名称的编码来形成有效的外部标识符。例如,一些其他未使用的字符或字符序列可以用于在通用字符名称中编码\u。扩展字符可能会产生一个长的外部标识符。
...

附录D

(规范)

标识符通用字符名

1 此子句列出在标识符的通用字符名称中有效的十六进制代码值。

D.1允许的字符范围

1 00A8,00AA,00AD,00AF,00B2−00B5,00B7−00BA,00BC−00BE,00C0−00D6,00D8−00F6,00F8−00FF
2 0100−167F、1681−180D、180F−1FFF
3 200B−200D、202A−202E、203F−2040、2054、2060−206F
4 2070−218F、2460−24FF、2776−2793、2C00−2DFF、2E80−2FFF
5 3004−3007、3021−302F、3031−303F
6 3040−D7FF
7 F900−FD3D、FD40−FDCF、FDF0−FE44、FE47−FFFD
8 10000−1FFFD、20000−2FFFD、30000−3FFFD、40000−4FFFD、50000−5FFFD、60000−6FFFD、70000−7FFFD、80000−8FFFD、90000−9FFFD、A0000−AFFFD、B0000−BFFFD、C0000−CFFFD、D0000−DFFFD、E0000−EFFFD

D.2最初不允许的字符范围

1 0300−036F、1DC0−1DFF、20D0−20FF、FE20−FE2F

idv4meu8

idv4meu83#

标识符(包括宏名称)的语法在C2011标准的第6.4.2节中给出,并根据附录D.1进行解释。这些规定认为,每个标识符可以包含下划线、大写和小写拉丁字母、十进制数字、构成“通用字符名称”的字符序列(受限制)、* 和实现定义的任何其他字符
通用字符名(UCN)是Unicode转义序列,类似于Java、Python和其他一些语言提供的转义序列:它们以反斜杠(\)开头,后跟uU,以及四个或八个十六进制数字。对于可以使用的特定十六进制数字序列存在一些限制,一些是一般性的,其他特定于标识符上下文。但是请注意,
从语法上 *,UCN的规定允许在标识符中出现的唯一附加字符是反斜杠;UCN中可以出现的所有其他字符也允许出现在UCN上下文之外的标识符中。
因此,从语法上讲,并将讨论限制在标准要求在标识符中允许的字符上,下划线,(无重音)拉丁字母,十进制数字和反斜杠是C要求在标识符中必须支持的唯一字符。只有在UCN的上下文中才需要支持反斜杠,并且并非所有有效的UCN都允许出现在标识符中。此外,该标准不要求支持数字作为标识符的第一个字符。
另一方面,该标准在允许标识符中包含“其他实现定义的字符”方面相当宽松,包括作为第一个字符。即使是不能作为身份识别特征第一个字符的十进制数字,原则上也可根据本条文的规定,由实施时斟酌决定在该位置使用。如果你希望你的代码在实现之间是可移植的,那么你将避免在任何地方依赖这个规定。如果你想知道你的特定实现允许哪些字符,那么你必须查阅它的文档。
每个符合标准的实现都必须记录其行为,这些行为与标准声明为实现定义的每个细节有关。例如,GCC的文档指定在大多数目标体系结构上允许标识符中使用美元符号($)。您自己也链接并引用了Clang的文档中相同的实现定义的细节,这更自由-它允许所有可以通过UCN在标识符中表示的字符也可以通过UTF-8字节序列表示。在许多情况下,如果显示或打印包含此类字节序列的源代码,它们将呈现为单个显示字符。

fcipmucu

fcipmucu4#

如前所述,C11 Standard定义了几个允许的Unicode字符范围。

  • 00A8,00AA,00AD,00AF,00B2−00B5,00B7−00BA,00BC−00BE,00C0−00D6,00D8−00F6,00F8−00FF
  • 0100−167F、1681−180D、180F−1FFF
  • 200B−200D、202A−202E、203F−2040、2054、2060−206F
  • 2070−218F、2460−24FF、2776−2793、2C00−2DFF、2E80−2FFF
  • 3004−3007、3021−302F、3031−303F
  • 3040−D7FF
  • F900−FD3D、FD40−FDCF、FDF0−FE44、FE47−FFFD
  • 10000−1FFFD、20000−2FFFD、30000−3FFFD、40000−4FFFD、50000−5FFFD、60000−6FFFD、70000−7FFFD、80000−8FFFD、90000−9FFFD、A0000−AFFFD、B0000−BFFFD、C0000−CFFFD、D0000−DFFFD、E0000−EFFFD

这也意味着有几个字符范围被排除在使用之外。
从你的例子:

  • 260E,来自“Miscellaneous Symbols”块:2600-26FF,这意味着您错过了all of these
  • 274C,来自“Dingbats”块:2700-27BFall of these,但其中一些是允许的(2776−2793
  • 21E7,并且来自“箭头”块:2190-21FF,这意味着您错过了all of these
  • 3013,来自“CJK Symbols and Punctuation”块:3000-303Fall these,但其中一些是允许的。
  • 🍎是1F34E,并且来自“杂项符号和象形文字”块:1F300-1F5FFall these,实际上应该可以工作

(可能是clang问题?顺便说一句,这不是显示在我的家庭电脑(Ubuntu),但在我的工作电脑(Win7))

相关问题