我在extern
和extern "C"
上进行了一些试验,偶然地在其中一个标识符上出现了错字-$
被偷了。当我编译代码并得到一个未定义符号的错误,最终看到了导致它的原因,这使我产生了好奇,如果它真的可以编译。猜猜是什么-actually实际上是 did 进行编译的。
根据我之前阅读的文档,标识符规则基本上是:
a-z
,A-Z
或0-9
和_
。但是这个编译的很好-也没有警告显示:
void __this$is$a$mess() {}
int main() { __this$is$a$mess(); }
观看时:
Ingwie@Ingwies-Macbook-Pro.local /tmp $ clang y.c
Ingwie@Ingwies-Macbook-Pro.local /tmp $ nm a.out
0000000100000f90 T ___this$is$a$mess
0000000100000000 T __mh_execute_header
0000000100000fa0 T _main
U dyld_stub_binder
我可以很清楚地看到符号名称。
那么,为什么按照Clang的要求我可以做到这一点,尽管按照ANSI标准,它不应该这样做?甚至我安装的GCC 6都没有警告或错误。
哪种编译器将允许使用哪种类型的标识符-为什么呢?
答案 0 :(得分:6)
2018 C标准中的标识符规则包括:
_
,a
至z
,A
至Z
,是一个通用的-字符名称,或“其他实现定义的字符”。0
至9
。\u
,后接四个十六进制数字,或者为\U
,后接八个十六进制数字,指定https://github.com/aframevr/aframe/blob/master/src/components/cursor.js#L213字符。因此,如果实现允许$
,则这是该实现的有效字符。您可以使用它,但是它可能无法移植到其他实现中。 C标准要求实现以接受列出的特定字符,但允许允许它们接受更多字符。通常,应将C标准视为一个开放的区域,而不是一个围墙花园:行为是在该区域内定义的,但是您不能在障碍处停下来。您可能会自行承担风险,承担责任。
您所教的规则是有关可移植内容的规则,而不是C标准要求实现以限制您使用的内容的规则。
C标准定义了严格符合规范的代码(大致上讲,该代码应在任何C实现中都适用)和 conforming code (其适用于任何C实现中的代码)至少一个C实现。合格代码仍然是C代码。因此,您所教的规则是严格遵守代码的。
通常,您应该更喜欢编写严格合规的代码,并且仅在受益(速度,在特定平台上的开发简便性,无论如何)值得(损失可移植性)时才使用其他功能。
答案 1 :(得分:5)
根据我之前阅读的文档, 标识符基本上是:
- 开头没有双下划线-因为这些都是保留的。
- 没有下划线和大写字母-也保留。
此类标识符确实是保留的,但这意味着您不能声明或定义它们,不是它们不能成为标识符,或者它们不一定有意义。
- 必须以字母和非数字开头。
字母确实是非数字,但并非所有非数字都是字母。 _
字符是一个很好的例子。
- 不得超过31个字符。
这不是语言的正式限制。 C要求实现在外部标识符中至少支持31个重要个字符。不能保证仅在32 nd 字符或更高版本上不同的两个外部标识符被识别为不同的,但是它们不会成为标识符。此外,实现必须在内部标识符中识别出至少63个有效字符,而且这些标识符可能更长。
某些实现可以识别更重要的字符,甚至可以识别无穷大的数字。
- 可能包含a-z,A-Z或0-9和_。
是的,但也可以明确地 包含其他实现定义的字符。特别是$
字符是相当普遍允许的字符。
那么为什么Clang让我这样做,尽管是通过ANSI 标准,应该不?我安装的GCC 6都没有发出警告 或与此有关的错误。
该标准绝不表示不允许包含$
字符的标识符。它明确允许实现接受该字符以及标识符中的几乎任何其他字符,尽管有些不能务实地被接受,因为允许它们会引起歧义。使用包含此类字符的标识符的程序不会因此而失败,而接受包含它们的实现也不会因此而因此而失败。这样的程序确实不能严格遵守,但是,该术语是由标准定义的。