为什么C中的某些函数有下划线前缀?

ped*_*tos 9 c sockets networking function

我最近开始用C学习网络,我看到一些以下划线开头的函数_function() - 这究竟是什么意思?我也看到了这个:

 struct sockaddr_in  {  

__SOCKADDR_COMMON (sin_);  

 in_port_t sin_port;    

 struct in_addr sin_addr;    

 unsigned char sin_zero[sizeof (struct sockaddr) - 

 __SOCKADDR_COMMON_SIZE -  

sizeof (in_port_t) -         

sizeof (struct in_addr)];  

};
Run Code Online (Sandbox Code Playgroud)

这部分代码意味着什么:

__SOCKADDR_COMMON (sin_);

unsigned char sin_zero[sizeof (struct sockaddr) - 

 __SOCKADDR_COMMON_SIZE -  

sizeof (in_port_t) -         

sizeof (struct in_addr)];
Run Code Online (Sandbox Code Playgroud)

Die*_*Epp 17

下划线前缀保留用于编译器和标准库使用的函数和类型.标准库可以自由使用这些名称,因为它们永远不会与正确的用户程序冲突.

另一方面是不允许您定义以下划线开头的名称.

嗯,这是规则的要点.实际的规则是这样的:

  • 您不能在名称以下划线开头的全局范围内定义任何标识符,因为这些标识符可能与隐藏(私有)库定义冲突.所以这在您的代码中无效:

    #ifndef _my_header_h_
    #define _my_header_h_ // wrong
    int _x; // wrong
    float _my_function(void); // wrong
    #endif
    
    Run Code Online (Sandbox Code Playgroud)

    但这是有效的:

    #ifndef my_header_h
    #define my_header_h // ok
    int x; // ok
    float my_function(void) { // ok
        int _x = 3; // ok in function
    }
    struct my_struct {
        int _x; // ok inside structure
    };
    #endif
    
    Run Code Online (Sandbox Code Playgroud)
  • 您不能在名称以两个下划线开头的任何范围内定义任何标识符,也不能在大写字母后跟一个下划线.所以这是无效的:

    struct my_struct {
        int _Field; // Wrong!
        int __field; // Wrong!
    };
    void my_function(void) {
        int _X; // Wrong!
        int __y; // Wrong!
    }
    
    Run Code Online (Sandbox Code Playgroud)

    但这没关系:

    struct my_struct {
        int _field; // okay
    };
    void my_function(void) {
        int _x; // okay
    }
    
    Run Code Online (Sandbox Code Playgroud)

实际上还有一些规则,只是为了使事情变得复杂,但上面的规则是最常被违反和最容易记住的.

  • @CraigEstey *“_”是 POSIX lib 约定* 请阅读 [C 标准](http://www.open-std.org/jtc1/sc22/wg14/www) 的 **7.1.3 保留标识符** /docs/n1570.pdf): *所有以下划线和大写字母或另一个下划线开头的标识符始终保留用于任何用途。*和*以下划线开头的所有标识符始终保留用作文件的标识符范围在普通名称空间和标签名称空间中。* 从逻辑上讲,您的论点与“我和鲨鱼一起游泳时浑身是血,但没有被吃掉。” 你说35年? (3认同)
  • @CraigEstey `__x` 和 `_X` 可能是宏。*但是,这是针对此类风险的 pgmr 选择。99.44% 的此类名称永远不会冲突* 如果存在冲突,祝您好运解决问题。鉴于将错误放入代码中是多么容易,为什么在上帝的美好地球上,你“永远”会做任何你知道可能会引入无法找出的错误的事情呢? (2认同)
  • @CraigEstey:那你很幸运。我只用 C 编写了大约 33 年的代码,我遇到了一些令人讨厌的问题,因为使用以下划线开头的名称的内部代码与同名的系统函数冲突。当你的代码定义了 `int _bind(int x, char y);` 并且系统定义并调用了 `char *_bind(void *p, char *a, int f);` 或类似的东西时,事情就会变得很糟糕——还有系统库代码最终会调用您的 `_bind()` 而不是预期的。那是几年前的事情(实际上是另一个千年),但我记得那件事和一些类似的案例。 (2认同)
  • @CraigEstey:这就是我提到“另一个千年”的原因——如今的问题往往不同,但名称的选择仍然会引起问题。这是少数(我认为少于六个)干扰前导下划线名称的案例之一。(我要补充一点,在本地使用 `_bind()` 作为名称不是我的选择,并且在移植到新平台之前它不会引起问题。)我遇到了更多关于 `_t` 的问题POSIX 保留的类型的后缀。从理论上讲,您必须主动激活 POSIX 功能 — 自动执行“-std=gnu11”。_[…继续…]_ (2认同)
  • _[…继续…]_ POSIX 规则就在那里,因此如果您违反它们并且受到伤害,您就不会卷土重来。在很大程度上,C 规则也是如此。他们还指导系统的开发人员——如果他们遵守规则,如果用户也遵守规则,他们就不会伤害他们的用户。棘手的一点是,新手看着系统头文件并认为“哦,我也应该这样写我的头文件”,然后立即打破为防止用户和系统提供者相互伤害而设置的所有规则。归根结底,这是一个教育问题。所以可以提供帮助。 (2认同)
  • @CraigEstey:关于你的第一条评论:使用`static ... _x` 仍然*不行* 的原因是因为库可能在你正在使用的头文件中使用`_x`,然后你会得到`_x` 的多个不兼容声明的错误。你说评估风险是由程序员决定的,这是正确的,但只是事实的一部分——程序员也应该评估收益。假设您没有根深蒂固的约定,在名称中使用前导下划线的好处是零。我希望使用此类名称的代码无法通过代码审查。 (2认同)

sle*_*ica 5

前导下划线通常表示以下三种情况之一:

  1. 该定义不是C标准的一部分,因此它不可移植
  2. 该定义是库或编译器的内部定义,不应从外部使用
  3. 不应轻易使用该定义,因为它意味着需要额外知识的一些风险或必要的配置.

在这种情况下,__SOCKADDR_COMMON是(2):内部定义,struct sockaddr_in类型的一部分,是您通常从userland访问的定义.