50
extern int ether_hostton (__const char *__hostname, struct ether_addr *__addr)
 __THROW;

我在 Linux 机器上的 /usr/include/netinet/ether.h 中找到了上述函数定义。

有人能解释一下 const(关键字)、addr(标识符)和最后 __THROW 前面的双下划线是什么意思吗?

4

4 回答 4

74

在 C 中,以下划线开头的符号后跟大写字母或另一个下划线保留用于实现。作为 C 语言的用户,您不应创建任何以保留序列开头的符号。在 C++ 中,限制更为严格;用户不得创建包含双下划线的符号。

鉴于:

extern int ether_hostton (__const char *__hostname, struct ether_addr *__addr)
__THROW;

__const表示法允许与此代码一起使用的编译器支持原型表示法但对 C89 标准关键字没有正确理解的可能性(有点不太可能)const。宏仍然可以检查编译器autoconf是否支持const; 这段代码可以与不支持的损坏的编译器一起使用。

使用__hostname__addr是对您,即标头用户的保护措施。如果您使用 GCC 和该-Wshadow选项进行编译,编译器会在任何局部变量隐藏全局变量时警告您。如果该函数仅用于hostname代替__hostname,并且如果您有一个名为 的函数hostname(),则会有阴影。通过使用为实现保留的名称,不会与您的合法代码发生冲突。

的使用__THROW意味着在某些情况下,可以使用某种“抛出规范”来声明代码。这不是标准 C;它更像是 C++。但是,只要头文件之一(或编译器本身)定义__THROW为空,或者定义为标准 C 语法的某些特定于编译器的扩展,该代码就可以与 C 编译器一起使用。


C 标准 (ISO 9899:1999) 的第 7.1.3 节说:

7.1.3 保留标识符

每个标头声明或定义其相关子条款中列出的所有标识符,并可选地声明或定义其相关的未来库方向子条款中列出的标识符和始终保留用于任何用途或用作文件范围标识符的标识符。

— 以下划线和大写字母或另一个下划线开头的所有标识符始终保留用于任何用途。

— 所有以下划线开头的标识符始终保留用作普通和标记名称空间中具有文件范围的标识符。

— 如果包含任何相关的标题,则保留以下任何子条款(包括未来的库方向)中的每个宏名称以供指定使用;除非另有明确说明(见 7.1.4)。

— 以下任何子条款(包括未来的库方向)中具有外部链接的所有标识符始终保留用作具有外部链接的标识符。154)

— 在以下任何子条款(包括未来的库方向)中列出的每个具有文件范围的标识符都保留用作宏名称和在同一名称空间中作为具有文件范围的标识符,如果包括其任何关联的头文件。

没有保留其他标识符。如果程序在保留标识符的上下文中声明或定义标识符(7.1.4 允许的除外),或将保留标识符定义为宏名称,则行为未定义。

如果程序删除(用#undef)上面列出的第一组中标识符的任何宏定义,则行为未定义。

脚注 154) 具有外部链接的保留标识符列表包括errnomath_errhandlingsetjmpva_end.


另请参阅关于在 C++ 标识符中使用下划线的规则是什么;许多相同的规则适用于 C 和 C++,尽管嵌入式双下划线规则仅在 C++ 中,如本答案顶部所述。


C99 基本原理

C99 基本原理说:

7.1.3 保留标识符

为了给实现者将库函数打包到文件中的最大自由度,库定义的所有外部标识符都保留在托管环境中。这意味着,实际上,用户提供的外部名称不能与库名称匹配,即使用户函数具有相同的规范也是如此。因此,例如,strtod可以在与 相同的对象模块中定义printf,而不必担心会发生链接时冲突。同样,strtod可能会调用printf,或者printf可能会调用strtod,无论出于何种原因,不用担心会调用错误的函数。

为实现者保留的还有以下划线开头的所有外部标识符,以及以下划线开头的所有其他标识符,后跟大写字母或下划线。这为编写库正常工作所需的众多幕后非外部宏和函数提供了名称空间。

除了这些例外,标准向程序员保证所有其他标识符都是可用的,当将程序从一个实现移动到另一个5时,不用担心出现意外冲突。请特别注意,以下划线开头的内部标识符的部分名称空间可供用户使用:翻译实现者并不是唯一使用“隐藏”名称的人。C 在许多方面都是一种可移植的语言,以至于“名称空间污染”问题一直是并且是编写完全可移植代码的主要障碍之一。因此,标准确保typedef仅当显式包含相关标头时才保留宏和名称。

5见 §6.2.1 讨论了实施者为信守承诺而应采取的一些预防措施。另请注意,结构中定义的任何实现定义的成员名称都<time.h>必须<locale.h>以下划线开头,而不是遵循这些结构中其他名称的模式。

§6.2.1 标识符范围的基本原理的相关部分是:

尽管函数原型中标识符的范围开始于它的声明并结束于该函数的声明符的末尾,但预处理器会忽略此范围。因此,原型中与现有宏具有相同名称的标识符被视为对该宏的调用。例如:

    #define status 23
    void exit(int status);

产生错误,因为预处理后的原型变成

   void exit(int 23);

也许更令人惊讶的是如果定义了状态会发生什么

   #define status []

然后得到的原型是

   void exit(int []);

这在语法上是正确的,但在语义上与意图完全不同。

为了保护实现的标头原型免受此类误解,实现者必须编写它们以避免这些意外。可能的解决方案包括不在原型中使用标识符,或者在保留的名称空间中使用名称(例如__status_Status)。

另请参阅 PJ Plauger The Standard C Library (1992),了解对名称空间规则和库实现的广泛讨论。这本书引用了 C90,而不是标准的任何更高版本,但其中的大部分实施建议直到今天仍然有效。

于 2009-09-19T19:28:56.177 回答
19

带有双前导下划线的名称保留供实现使用。这并不一定意味着它们本身是内部的,尽管它们通常是内部的。

这个想法是,您不允许使用以 开头的任何名称__,因此实现可以在宏扩展之类的地方自由使用它们,或者在语法扩展的名称中使用它们(例如__gcnew,不是 C++ 的一部分,但 Microsoft 可以添加它让 C++/CLI 确信现有代码中不应该有类似int __gcnew;的东西会停止编译)。

要了解这些特定扩展的含义,即__const您需要查阅特定编译器/平台的文档。在这种特殊情况下,您可能应该将文档中的原型(例如http://www.kernel.org/doc/man-pages/online/pages/man3/ether_aton.3.html)视为函数的接口,并且忽略实际标题中出现的__const和装饰。__THROW

于 2009-09-19T19:03:54.620 回答
3

按照某些库的约定,这表示特定符号供内部使用,而不是作为库公共 API 的一部分。

于 2009-09-19T18:35:33.220 回答
2

__const 中的下划线表示该关键字是编译器扩展并且使用它是不可移植的(const 关键字是在后来的版本中添加到 C 中的,我认为是 89)。__THROW 也是某种扩展,我假设如果使用 gcc,它会被定义为一些 __attribute__(something),但我不确定,也懒得检查。__addr 可以表示程序员想要的任何含义,它只是一个名称。

于 2009-09-19T18:50:04.643 回答