什么构成“有效”C标识符?

时间:2015-12-16 18:00:40

标签: c syntax clang naming-conventions identifier

@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's docs refer to the issue,仅说明......

  

...支持C99和C ++中的扩展标识符。此功能允许标识符包含某些Unicode字符,如活动语言标准所指定;这些字符可以使用UTF-8编码直接写入源文件,也可以使用通用字符名称(\ u00E0,\ U000000E0)引用。

所以,我想我在问......什么是“主动语言标准”,我怎样才能找到合法标识符的权威来源。

我创建了following code只是为了看看clang会对它做些什么。在测试的大约63488个可能的标识符中,23个发出了警告,9506个生成了错误。这使得将近54,000个有效字符用于标识符。当然够了,但谁被砍了?为什么?

3 个答案:

答案 0 :(得分:8)

C 2011 standard

6.4.2标识符

6.4.2.1概述
...
3标识符中的每个通用字符名称应指定其编码的字符 在ISO / IEC 10646中属于D.1中规定的范围之一。 71)初始字符 不应是指定编码所属字符的通用字符名称 D.2中规定的范围之一。实现可以允许多字节字符 不属于标识符中出现的基本源字符集的一部分;哪个角色 它们与通用字符名称的对应关系是实现定义的。
... 71)在链接器不能接受扩展字符的系统上,通用字符的编码 name可用于形成有效的外部标识符。例如,一些否则未使用 字符或字符序列可用于在通用字符名称中对\ 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

答案 1 :(得分:6)

标识符的语法(包括宏名称)在C2011标准的第6.4.2节中给出,如附录D.1所​​解释。这些规定认为每个标识符可以包含下划线,大写和小写拉丁字母,十进制数字,字符序列构成"通用字符名称" (受限制),和实现定义的任何其他字符

通用字符名称(UCN)是类似于Java,Python和其他一些语言提供的Unicode转义序列:它们以反斜杠(\)开头,后跟uU,分别为四个或八个十六进制数字。可以使用的特定十六进制数字序列存在一些限制,一些是通用的,另一些是特定于标识符上下文的。但请注意,语法,UCN规定允许出现在标识符中的唯一附加字符是反斜杠;所有其他可以出现在UCN中的字符也允许在UCN上下文之外的标识符中使用。

因此,从语法上讲,将讨论限制在标识符中允许标准所允许的字符,下划线,(非重音)拉丁字母,十进制数字和反斜杠是C要求必须支持的唯一字符。身份标识。仅在UCN的上下文中需要支持反斜杠,并且标识符中不允许所有有效的UCN。此外,该标准不需要支持数字作为标识符的第一个字符。

另一方面,标准在允许"其他实现定义的字符"中非常宽松。在标识符中,包括作为第一个字符。即使十进制数字,否则不能是标识符中的第一个字符,原则上可以根据本规定在该位置允许,由执行决定。如果您希望代码在实现之间可移植,那么您将避免在任何地方依赖此规定。如果您想知道您的特定实现允许哪些字符,那么您必须查阅其文档。

每个符合标准的实现必须记录其标准声明为实现定义的每个细节的行为。例如,GCC's documentation指定在大多数目标体系结构的标识符中允许使用美元符号($)。您自己链接并引用了Clang的相同实现定义细节的文档,它更加自由 - 它允许通过UCN在标识符中表示的所有字符也可以由UTF-8字节序列表示。在许多情况下,如果您显示或打印包含此类字节序列的源代码,它们将呈现为单个显示字符。

答案 2 :(得分:4)

如前所述,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,来自"杂项符号"阻止:2600-26FF,这意味着您错过了all of these
  • ❌是274C,来自" Dingbats"阻止:2700-27BF all of these,但其中一些是允许的(2776−2793
  • ⇧是21E7,来自"箭头"阻止:2190-21FF,这意味着您错过了all of these
  • 〓是3013,来自" CJK符号和标点"阻止:3000-303Fall these,但其中一些是允许的。
  • 1F34E,来自"其他符号和象形文字"阻止:1F300-1F5FF这是all these并且实际应该工作(可能是clang问题?顺便说一下,这不会显示在我的家用电脑(Ubuntu)上,而是显示在我的工作PC上( Win7的))