为什么某些声明为 extern 的函数和头文件未包含在 Git 源代码中的源代码中?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18171373/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-19 08:53:43  来源:igfitidea点击:

Why are some functions declared extern and header file not included in source in Git source code?

cgitcoding-style

提问by rsjethani

I wanted to see the source code of a real world application to understand good programming practices etc. So I chose Git and downloaded the source for version 1.8.4.

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

After randomly browsing through various files something caught my attention in these two files: strbuf.hstrbuf.c

在随机浏览各种文件后,这两个文件引起了我的注意:strbuf.h strbuf.c

These two files apparently define an API with this documentation.

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

I have two questions :

我有两个问题:

  1. Why the function declarations at line 16,17,18,19 & global variable at line 6 in 'strbuf.h' declared extern ?

  2. Why "strbuf.h" is not #included in strbuf .c ?

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

  2. 为什么“strbuf.h”不包含在 strbuf .c 中?

I as a novice programmer have always learned that you write function definitions in a .c file whereas the function declarations,macros,inlines etc. are written in a .h file which is then #included in every .c file which wants to use these functions etc.

作为一名新手程序员,我一直都知道您在 .c 文件中编写函数定义,而函数声明、宏、内联等编写在 .h 文件中,然后 #included 在每个想要使用这些的 .c 文件中功能等

Can anyone please explain this?

任何人都可以解释一下吗?

回答by torek

strbuf.cincludes cache.hand cache.hincludes strbuf.h, so your premise for question 2 (that strbuf.cdoes not include strbuf.h) is wrong: it does include it, just not directly.

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

externapplied to functions

extern应用于函数

The externkeyword is never required for function declarations, but it does have an effect: it declares that the identifier naming the function (i.e., the function's name) has the same linkage as any previously visible declaration, or if no such declaration is visible, that the identifier has external linkage. This rather confusing phrasing really means that, given:

extern关键字永远不会要求函数声明,但它确实有一个作用:它声明的标识符命名的函数(即函数名)具有相同的链接任何以前可见的声明,或者如果没有这样的声明是可见的,即标识符具有外部链接。这种相当令人困惑的措辞实际上意味着,鉴于:

static int foo(void); extern int foo(void);

the second declaration of fooalso declares it static, giving it internal linkage. If you write:

的第二个声明foo也声明了它static,给它内部链接。如果你写:

static int foo(void); int foo(void); /* wrong in 1990s era C */

you have declared it first as having internal linkage, and then second as having external linkage, and in pre-1999 versions of C,1that produces undefined behavior. In one sense, then, the externkeyword adds some safety (at the price of confusion) as it can mean staticwhen necessary. But you could always write staticagain, and externis not a panacea:

您首先将其声明为具有内部链接,然后第二个声明为具有外部链接,并且在 1999 年之前的 C 版本中,1会产生未定义的行为。从某种意义上说,extern关键字增加了一些安全性(以混淆为代价),因为它static在必要时可能意味着。但是你总是可以再写static一遍,而且extern不是万能的:

extern int foo(void); static int foo(void); /* ERROR */

This third form is still erroneous. The first externdeclaration has no previous visible declaration, so foohas external linkage, and then the second staticdeclaration gives foointernal linkage, producing undefined behavior.

这第三种形式仍然是错误的。第一个extern声明没有先前的可见声明,因此foo有外部链接,然后第二个static声明提供foo内部链接,产生未定义的行为。

In short, externis not required on function declarations. Some people just prefer it for style reasons.

简而言之,extern在函数声明中不需要。有些人只是出于风格原因更喜欢它。

(Note: I'm leaving out extern inlinein C99, which is kind of weird, and implementations vary. See http://www.greenend.org.uk/rjk/2003/03/inline.htmlfor more details.)

(注意:我extern inline在 C99 中遗漏了,这有点奇怪,而且实现也各不相同。有关更多详细信息,请参阅http://www.greenend.org.uk/rjk/2003/03/inline.html。)

externapplied to variable declarations

extern应用于变量声明

The externkeyword on a variable declaration has multiple different effects. First, as with function declarations, it affects the linkage of the identifier. Second, for an identifier outside any function (a "global variable" in one of the two usual senses), it causes the declaration to be a declaration, rather than a definition, provided the variable is not also initialized.

extern变量声明上的关键字有多种不同的效果。首先,与函数声明一样,它会影响标识符的链接。其次,对于任何函数之外的标识符(两种通常意义上的“全局变量”之一),它会导致声明成为声明,而不是定义,前提是变量没有也被初始化。

For variables inside a function (i.e., with "block scope"), such as somevarin:

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

void f(void) {
    extern int somevar;
    ...
}

the externkeyword causes the identifier to have some linkage (internal or external) instead of "no linkage" (as for automatic-duration local variables). In the process, it also causes the variable itself to have static duration, rather than automatic. (Automatic-duration variables never have linkage, and always have block scope, rather than file scope.)

extern关键字使标识符有一些键(内部或外部),而不是“无连接”(如用于自动持续时间的局部变量)。在这个过程中,它也导致变量本身具有静态持续时间,而不是自动的。(自动持续时间变量永远没有链接,并且始终具有块作用域,而不是文件作用域。)

As with function declarations, the linkage externassigns is internal if there is a previous visible internal-linkage declaration, and external otherwise. So the xinside f()here has internal linkage, despite the externkeyword:

与函数声明一样,extern如果存在先前可见的内部链接声明,则链接分配是内部的,否则是外部的。所以这里的x内部f()有内部链接,尽管有extern关键字:

static int x;
void f(void) {
    extern int x; /* note: don't do this */
    ...
}

The only reason to write this kind of code is to confuse other programmers, so don't do it. :-)

写这种代码的唯一原因是为了迷惑其他程序员,所以不要这样做。:-)

In general, the reason to annotate "global" (i.e., file-scope, static-duration, external-linkage) variables with the externkeyword is to prevent that particular declaration from becoming a definition. C compilers that use the so-called "def/ref" model get indigestion at link time when the same name is defined more than once. Thus, if file1.csays int globalvar;and file2.calso says int globalvar;, both are definitions and the code may not compile (although most Unix-like systems use the so-called "common model" by default, which makes this work anyway). If you are declaring such a variable in a header file—which is likely to be included from many different .cfiles—use externto make that declaration "just a declaration".

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

One, and only one, of those .cfiles can then declare the variable again, leaving off the externkeyword and/or including an initializer. Or, some people prefer a style in which the header file uses something like this:

这些.c文件中只有一个可以再次声明变量,省略extern关键字和/或包括初始化程序。或者,有些人更喜欢头文件使用这样的样式:

/* foo.h */
#ifndef EXTERN
# define EXTERN extern
#endif
EXTERN int globalvar;

In this case, one (and only one) of those .cfiles can contain the sequence:

在这种情况下,这些.c文件中的一个(并且只有一个)可以包含以下序列:

#define EXTERN
#include "foo.h"

Here, since EXTERNis defined, the #ifndefturns off the subsequent #defineand the line EXTERN int globalvar;expands to just int globalvar;so that this becomes a definition rather than a declaration. Personally, I dislike this coding style, although it does satisfy the "don't repeat yourself" principle. Mostly I find the uppercase EXTERNmisleading, and this pattern is unhelpful with initialization. Those who favor it usually wind up adding a second macro to hide the initializers:

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

#ifndef EXTERN
# define EXTERN extern
# define INIT_VAL(x) /*nothing*/
#else
# define INIT_VAL(x) = x
#endif

EXTERN int globalvar INIT_VAL(42);

but even this falls apart when the item to be initialized needs a compound initializer (e.g., a structthat should be initialized to { 42, 23, 17, "hike!" }).

但是当要初始化的项目需要复合初始化器(例如,struct应该初始化为 的 a { 42, 23, 17, "hike!" })时,即使这样也会崩溃。

(Note: I've deliberately glossed over the whole "tentative definition" thing here. A definition without an initializer is only "tentatively defined" until the end of the translation unit. This allows certain kinds of forward-references that are otherwise too difficult to express. It's not normally very important.)

(注意:我在这里故意掩盖了整个“暂定定义”的东西。没有初始化器的定义只是“暂定定义”直到翻译单元结束。这允许某些类型的前向引用否则太困难来表达。通常不是很重要。)

including the header that declares function fin code that defines function f

f在定义函数的代码中包括声明函数的头文件f

This is always a good idea, for one simple reason: the compiler will compare the declarationof f()in the header against the definitionof f()in the code. If the two do not match (for any reason—typically a mistake in initial coding, or a failure to update one of the two during maintenance, but occasionally simply due to Cat Walked On Keyboard Syndrome or similar), the compiler can catch the mistake at compile time.

这始终是一个好主意,因为一个简单的原因:编译器将比较申报f()在头对定义f()代码。如果两者不匹配(出于任何原因——通常是初始编码中的错误,或者在维护期间未能更新两者之一,但偶尔只是由于 Cat Walked On Keyboard Syndrome 或类似的原因),编译器可以捕获错误在编译时。



1The 1999 C standard says that omitting the externkeyword in a function declaration means the same thing as using the externkeyword there. This is much simpler to describe, and means you get defined (and sensible) behavior instead of undefined (and therefore maybe-good maybe-bad behavior).

11999 C 标准规定,extern在函数声明中省略关键字与在函数声明中使用extern关键字的含义相同。这更容易描述,并且意味着您得到定义(和明智)的行为而不是未定义的(因此可能是好的可能是坏的行为)。