C 为什么Git源代码中的源代码中不包含一些声明为extern和头文件的函数?

C 为什么Git源代码中的源代码中不包含一些声明为extern和头文件的函数?,c,git,coding-style,C,Git,Coding Style,我想看看真实世界应用程序的源代码,以了解良好的编程实践等。因此,我选择了Git并下载了版本1.8.4的源代码 在随机浏览了各种文件后,以下两个文件引起了我的注意: 这两个文件显然定义了一个API 我有两个问题: 为什么“strbuf.h”中第16、17、18、19行的函数声明和第6行的全局变量声明为extern 为什么strbuf.c中不包括“strbuf.h” 作为一名新手程序员,我一直了解到,您可以在.c文件中编写函数定义,而函数声明、宏、内联线等都是在.h文件中编写的,该文件包含在每个希望

我想看看真实世界应用程序的源代码,以了解良好的编程实践等。因此,我选择了Git并下载了版本1.8.4的源代码

在随机浏览了各种文件后,以下两个文件引起了我的注意:

这两个文件显然定义了一个API

我有两个问题:

  • 为什么“strbuf.h”中第16、17、18、19行的函数声明和第6行的全局变量声明为extern

  • 为什么strbuf.c中不包括“strbuf.h”

  • 作为一名新手程序员,我一直了解到,您可以在.c文件中编写函数定义,而函数声明、宏、内联线等都是在.h文件中编写的,该文件包含在每个希望使用这些函数的.c文件中


    谁能解释一下吗?

    strbuf.c
    包括
    cache.h
    cache.h
    包括
    strbuf.h
    ,所以你对问题2的前提(即
    strbuf.c
    不包括
    strbuf.h
    )是错误的:它确实包括它,只是不直接包括它

    extern
    应用于函数 函数声明从不需要
    extern
    关键字,但它确实有一个效果:它声明命名函数的标识符(即函数名称)与任何以前可见的声明具有相同的链接,或者如果没有此类声明可见,则该标识符具有外部链接。这种相当混乱的措辞实际上意味着:

    static int foo(void); extern int foo(void);
    
    foo
    的第二个声明也声明了它的
    static
    ,给了它内部链接。如果你写:

    static int foo(void); int foo(void); /* wrong in 1990s era C */
    
    您首先声明它具有内部链接,然后声明它具有外部链接,并且在1999年以前版本的C中,1会产生未定义的行为。因此,从某种意义上说,
    extern
    关键字增加了一些安全性(以混淆为代价),因为它在必要时可能意味着
    static
    。但是您可以再次编写
    静态
    ,而且
    外部
    并不是万能药:

    extern int foo(void); static int foo(void); /* ERROR */
    
    第三种形式仍然是错误的。第一个
    extern
    声明之前没有可见的声明,因此
    foo
    具有外部链接,然后第二个
    static
    声明提供
    foo
    内部链接,产生未定义的行为

    简而言之,
    extern
    不是函数声明所必需的。有些人只是出于时尚的原因而喜欢它

    (注意:我在C99中省略了
    extern-inline
    ,这有点奇怪,实现也各不相同。有关更多详细信息,请参阅。)

    extern
    应用于变量声明 变量声明中的
    extern
    关键字具有多种不同的效果。首先,与函数声明一样,它影响标识符的链接。第二,对于任何函数之外的标识符(两种通常意义之一的“全局变量”),如果变量未初始化,它将使声明成为声明,而不是定义

    对于函数内部的变量(即具有“块作用域”),例如
    somevar
    in:

    void f(void) {
        extern int somevar;
        ...
    }
    
    extern
    关键字使标识符具有某种链接(内部或外部),而不是“无链接”(对于自动持续时间局部变量)。在这个过程中,它还导致变量本身具有静态持续时间,而不是自动持续时间。(自动持续时间变量从不具有链接,并且始终具有块范围,而不是文件范围。)

    与函数声明一样,如果存在以前可见的内部链接声明,则链接
    extern
    赋值为内部,否则为外部。因此,这里的
    x
    内部
    f()
    具有内部链接,尽管有
    extern
    关键字:

    static int x;
    void f(void) {
        extern int x; /* note: don't do this */
        ...
    }
    
    编写这种代码的唯一原因是混淆其他程序员,所以不要这样做。:-)

    通常,使用
    extern
    关键字注释“全局”(即文件范围、静态持续时间、外部链接)变量的原因是为了防止该特定声明成为定义。使用所谓“def/ref”模型的C编译器在链接时多次定义同一名称时会消化不良。因此,如果
    file1.c
    intglobalvar
    file2.c
    也表示
    intglobalvar,两者都是定义,代码可能无法编译(尽管大多数类似Unix的系统默认使用所谓的“公共模型”,这使它仍然可以工作)。如果您在头文件中声明这样一个变量,它可能包含在许多不同的
    .c
    文件中,请使用
    extern
    使该声明“只是一个声明”

    这些
    .c
    文件中只有一个可以再次声明变量,不使用
    extern
    关键字和/或包含初始值设定项。或者,有些人更喜欢头文件使用这样的样式:

    /* foo.h */
    #ifndef EXTERN
    # define EXTERN extern
    #endif
    EXTERN int globalvar;
    
    在这种情况下,这些
    .c
    文件中的一个(且仅一个)可以包含以下序列:

    #define EXTERN
    #include "foo.h"
    
    这里,由于定义了
    EXTERN
    ,因此
    #ifndef
    关闭后续的
    #define
    和行
    EXTERN int globalvar扩展到仅
    intglobalvar,使其成为定义而不是声明。就我个人而言,我不喜欢这种编码风格,尽管它确实满足了“不要重复你自己”的原则。大多数情况下,我发现大写的
    EXTERN
    有误导性,这种模式对初始化没有帮助。支持它的人通常会添加第二个宏来隐藏初始值设定项:

    #ifndef EXTERN
    # define EXTERN extern
    # define INIT_VAL(x) /*nothing*/
    #else
    # define INIT_VAL(x) = x
    #endif
    
    EXTERN int globalvar INIT_VAL(42);
    
    但是,当要初始化的项需要复合初始值设定项(例如,应初始化为
    {42的
    struct
    )时,即使这样也会出现问题,