C 为什么Git源代码中的源代码中不包含一些声明为extern和头文件的函数?
我想看看真实世界应用程序的源代码,以了解良好的编程实践等。因此,我选择了Git并下载了版本1.8.4的源代码 在随机浏览了各种文件后,以下两个文件引起了我的注意: 这两个文件显然定义了一个API 我有两个问题: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文件中编写的,该文件包含在每个希望
谁能解释一下吗?
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
)时,即使这样也会出现问题,