人们为什么在C ++中这么多使用__(双下划线)


93

我浏览了一些开源C ++代码,发现在代码中使用了很多双下划线,主要是在变量名的开头。

return __CYGWIN__;

只是想知道这是有原因的,还是仅仅是某些人编写了代码样式?我认为我很难阅读。


2
为什么很难读?它的设计主要是与引号一样的分度符。我记得,它主要用于内置常量。
马修·沙利

1
不,这不是分隔符。下划线用于区分为实现保留的名称和用户源代码可以使用的名称。用户可以做的#define FOO 1,但他们不能这样做#define __FOO__ 1,因此实现可以自由使用的名称__FOO__为自己的宏,变量,函数等
乔纳森Wakely

我认为Matthew的意思是从风格上/视觉上来说是分度数,而不是功能上的分度数。这是一个有趣的假设,但鉴于我之前阅读的内容和乔纳森的回答,这是不正确的。
JMI MADISON

Answers:


127

来自C ++编程,规则和建议

根据ANSI-C标准,标识符中使用两个下划线(__)保留给编译器内部使用。

下划线(_)通常用于库函数的名称(例如“ _main”和“ _exit”)。为了避免冲突,请勿以下划线开头标识符。


1
该指南看起来像是在namespace介绍之前编写的。
cz

它也是来自伦敦皇家学院而不是来自C ++标准。这可能是个好建议。
stucash

1
@cz命名空间无关。系统标头可以定义以下划线开头的宏名称,例如_main
martinkunev

49

除非他们认为它们是“实现的一部分”,即标准库,否则它们就不应该。

这些规则是相当具体的,并且比其他一些建议更详细。

所有包含双下划线或下划线后跟大写字母的标识符都保留给所有范围的实现使用,即它们可能用于宏。

另外,所有其他以下划线开头的标识符(即,后跟另一个下划线或大写字母的标识符)都保留给全局范围内的实现。这意味着您可以在自己的名称空间或类定义中使用这些标识符。

这就是为什么Microsoft在大多数C ++标准不包含的核心运行库函数中使用带下划线的函数名,并用小写字母表示。这些函数名称保证不会与标准C ++函数或用户代码函数冲突。


1
在C ++中,我仅看到[lex.name],而全局名称为[global.names]。你能提供参考吗?谢谢
a.lasram 2014年

36

根据C ++标准,以一个下划线开头的标识符保留给库。以两个下划线开头的标识符保留给编译器供应商。


18
不仅如此:保留了在任何地方包含双下划线的标识符。17.4.3.1.2
史蒂夫·杰索普

在C ++中,我仅看到[lex.name],而全局名称为[global.names]。你能提供参考吗?谢谢
a.lasram 2014年

10

前面的评论是正确的。 __Symbol__通常是有用的编译器(或预处理器)供应商提供的魔术令牌。也许其中使用最广泛的是__FILE____LINE__,它们由C预处理程序扩展以指示当前的文件名和行号。当您要记录某种程序断言失败(包括错误的文本位置)时,这很方便。


8

这不是您在“普通”代码中要做的事情。这样可以确保编译器和系统库可以定义不会与您的符号冲突的符号。


5

双下划线保留给实现

投票最多的答案引用了C ++编程:规则和建议

“在标识符中使用两个下划线('__')保留给编译器根据ANSI-C标准在内部使用。”

然而,阅读一些C ++和C标准后,我无法找到下划线的任何提及被局限于只是编译器的内部使用。这些标准较为笼统,为实现保留了双下划线。

C ++

C ++(当前的工作草案,于2019-5-26访问)指出lex.name

  • 包含双下划线__或下划线后跟大写字母的每个标识符都保留给实现用于任何用途。
  • 每个以下划线开头的标识符都保留给实现,以用作全局名称空间中的名称。

C

尽管此问题特定于C ++,但我引用了C标准99和17的相关章节:

C99第7.1.3节

  • 所有以下划线,大写字母或另一个下划线开头的标识符始终保留供任何使用。
  • 在普通和标记名称空间中,所有以下划线开头的标识符始终保留为文件范围的标识符。

C17表示与C99相同的内容。

什么实现

对于C / C ++,该实现宽松地指代从用户源文件生成可执行文件所需的设置资源。这包括:

  • 预处理器
  • 编译器
  • 连接器
  • 标准库

示例实现

Wikipedia上提到了许多不同的C ++实现。(无锚链接,按ctrl + f“实现”)

这是Digital Mars的C / C ++实现示例,为它们的功能保留了一些关键字。


3

除了许多其他人都在回答的库之外,有些人还命名宏或#define值以供预处理器使用。这样可以更轻松地使用它,并且可以解决较早的编译器中的错误。

像其他提到的一样,它有助于防止名称冲突,并有助于在库变量和您自己的变量之间进行区分。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.