extern 关键字对 C 函数的影响

发布于 2024-07-19 07:06:55 字数 603 浏览 4 评论 0 原文

在 C 中,我没有注意到函数声明之前使用的 extern 关键字有任何影响。 起初,我认为在单个文件中定义 extern int f();强制您在文件范围之外实现它。 然而我发现:

extern int f();
int f() {return 0;}

extern int f() {return 0;}

编译都很好,没有来自 gcc 的警告。 我使用了gcc -Wall -ansi; 它甚至不接受 // 注释。

在函数定义之前使用extern有什么影响吗? 或者它只是一个可选关键字,对函数没有副作用。

在后一种情况下,我不明白为什么标准设计者选择在语法中添加多余的关键字。

编辑:澄清一下,我知道变量中有 extern 的用法,但我只是询问函数中的 extern强>。

In C, I did not notice any effect of the extern keyword used before function declaration.
At first, I thought that when defining extern int f(); in a single file forces you to implement it outside of the file's scope. However I found out that both:

extern int f();
int f() {return 0;}

and

extern int f() {return 0;}

compile just fine, with no warnings from gcc. I used gcc -Wall -ansi; it wouldn't even accept // comments.

Are there any effects for using extern before function definitions? Or is it just an optional keyword with no side effects for functions.

In the latter case I don't understand why did the standard designers chose to litter the grammar with superfluous keywords.

EDIT: To clarify, I know there's usage for extern in variables, but I'm only asking about extern in functions.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

屋檐 2024-07-26 07:06:55

我们有两个文件,foo.c 和 bar.c。

这是 foo.c

#include <stdio.h>

volatile unsigned int stop_now = 0;
extern void bar_function(void);

int main(void)
{
  while (1) {
     bar_function();
     stop_now = 1;
  }
  return 0;
}

现在,这是 bar.c

#include <stdio.h>

extern volatile unsigned int stop_now;

void bar_function(void)
{
   if (! stop_now) {
      printf("Hello, world!\n");
      sleep(30);
   }
}

如您所见,我们在 foo.c 和 bar.c 之间没有共享标头,但是 bar.c 在链接时需要在 foo.c 中声明一些内容,并且 foo.c链接时需要 bar.c 中的函数。

通过使用“extern”,您可以告诉编译器,它后面的任何内容都将在链接时找到(非静态); 不要在当前通道中为其保留任何内容,因为稍后会遇到它。 在这方面,函数和变量受到同等对待。

如果您需要在模块之间共享一些全局变量并且不想将其放在标头中/对其进行初始化,那么它非常有用。

从技术上讲,库公共标头中的每个函数都是“extern”,但是将它们标记为“extern”几乎没有任何好处,具体取决于编译器。 大多数编译器可以自己解决这个问题。 如您所见,这些函数实际上是在其他地方定义的。

在上面的例子中,main()只会打印一次hello world,但会继续输入bar_function()。 另请注意,bar_function() 在此示例中不会返回(因为它只是一个简单的示例)。 想象一下当信号被服务时 stop_now 被修改(因此,易失性),如果这看起来不够实用。

外部对象对于信号处理程序、您不想放入标头或结构中的互斥锁等非常有用。大多数编译器都会进行优化以确保它们不会为外部对象保留任何内存,因为它们知道它们'将把它保留在定义对象的模块中。 然而,同样,在对公共函数进行原型设计时,使用现代编译器来指定它没有什么意义。

We have two files, foo.c and bar.c.

Here is foo.c

#include <stdio.h>

volatile unsigned int stop_now = 0;
extern void bar_function(void);

int main(void)
{
  while (1) {
     bar_function();
     stop_now = 1;
  }
  return 0;
}

Now, here is bar.c

#include <stdio.h>

extern volatile unsigned int stop_now;

void bar_function(void)
{
   if (! stop_now) {
      printf("Hello, world!\n");
      sleep(30);
   }
}

As you can see, we have no shared header between foo.c and bar.c , however bar.c needs something declared in foo.c when it's linked, and foo.c needs a function from bar.c when it's linked.

By using 'extern', you are telling the compiler that whatever follows it will be found (non-static) at link time; don't reserve anything for it in the current pass since it will be encountered later. Functions and variables are treated equally in this regard.

It's very useful if you need to share some global between modules and don't want to put / initialize it in a header.

Technically, every function in a library public header is 'extern', however labeling them as such has very little to no benefit, depending on the compiler. Most compilers can figure that out on their own. As you see, those functions are actually defined somewhere else.

In the above example, main() would print hello world only once, but continue to enter bar_function(). Also note, bar_function() is not going to return in this example (since it's just a simple example). Just imagine stop_now being modified when a signal is serviced (hence, volatile) if this doesn't seem practical enough.

Externs are very useful for things like signal handlers, a mutex that you don't want to put in a header or structure, etc. Most compilers will optimize to ensure that they don't reserve any memory for external objects, since they know they'll be reserving it in the module where the object is defined. However, again, there's little point in specifying it with modern compilers when prototyping public functions.

水溶 2024-07-26 07:06:55

据我记得标准,默认情况下所有函数声明都被视为“extern”,因此无需显式指定它。

这并不会使这个关键字变得无用,因为它也可以与变量一起使用(在这种情况下 - 它是解决链接问题的唯一解决方案)。 但对于功能 - 是的,它是可选的。

As far as I remember the standard, all function declarations are considered as "extern" by default, so there is no need to specify it explicitly.

That doesn't make this keyword useless since it can also be used with variables (and it that case - it's the only solution to solve linkage problems). But with the functions - yes, it's optional.

枕花眠 2024-07-26 07:06:55

您需要区分两个不同的概念:函数定义和符号声明。 “extern”是一个链接修饰符,向编译器提示后面引用的符号的定义位置(提示是“不在这里”)。

如果我

extern int i;

在 C 文件中的文件范围(功能块之外)写入,那么您会说“该变量可能在其他地方定义”。

extern int f() {return 0;}

既是函数 f 的声明又是函数 f 的定义。 在这种情况下,定义会覆盖 extern。

extern int f();
int f() {return 0;}

首先是声明,然后是定义。

如果您想声明并同时定义文件作用域变量,则使用 extern 是错误的。 例如,

extern int i = 4;

会给出错误或警告,具体取决于编译器。

如果您明确希望避免定义变量,则使用 extern 非常有用。

让我解释一下:

假设文件 ac 包含:

#include "a.h"

int i = 2;

int f() { i++; return i;}

文件 ah 包含: 文件

extern int i;
int f(void);

bc 包含:

#include <stdio.h>
#include "a.h"

int main(void){
    printf("%d\n", f());
    return 0;
}

标头中的 extern 很有用,因为它在链接阶段告诉编译器,“这是一个声明,而不是一个定义”。 如果我删除 ac 中定义 i 的行,为其分配空间并为其赋值,则程序应该无法使用未定义的引用进行编译。 这告诉开发人员他已经引用了一个变量,但尚未定义它。 另一方面,如果我省略“extern”关键字,并删除 int i = 2 行,程序仍然可以编译 - i 将使用默认值定义为 0。

如果您没有显式地为文件作用域变量赋值,则文件作用域变量将隐式定义为默认值 0 或 NULL - 与在函数顶部声明的块作用域变量不同。 extern 关键字避免了这种隐式定义,从而有助于避免错误。

对于函数来说,在函数声明中,关键字确实是多余的。 函数声明没有隐式定义。

You need to distinguish between two separate concepts: function definition and symbol declaration. "extern" is a linkage modifier, a hint to the compiler about where the symbol referred to afterwards is defined (the hint is, "not here").

If I write

extern int i;

in file scope (outside a function block) in a C file, then you're saying "the variable may be defined elsewhere".

extern int f() {return 0;}

is both a declaration of the function f and a definition of the function f. The definition in this case over-rides the extern.

extern int f();
int f() {return 0;}

is first a declaration, followed by the definition.

Use of extern is wrong if you want to declare and simultaneously define a file scope variable. For example,

extern int i = 4;

will give an error or warning, depending on the compiler.

Usage of extern is useful if you explicitly want to avoid definition of a variable.

Let me explain:

Let's say the file a.c contains:

#include "a.h"

int i = 2;

int f() { i++; return i;}

The file a.h includes:

extern int i;
int f(void);

and the file b.c contains:

#include <stdio.h>
#include "a.h"

int main(void){
    printf("%d\n", f());
    return 0;
}

The extern in the header is useful, because it tells the compiler during the link phase, "this is a declaration, and not a definition". If I remove the line in a.c which defines i, allocates space for it and assigns a value to it, the program should fail to compile with an undefined reference. This tells the developer that he has referred to a variable, but hasn't yet defined it. If on the other hand, I omit the "extern" keyword, and remove the int i = 2 line, the program still compiles - i will be defined with a default value of 0.

File scope variables are implicitly defined with a default value of 0 or NULL if you do not explicitly assign a value to them - unlike block-scope variables that you declare at the top of a function. The extern keyword avoids this implicit definition, and thus helps avoid mistakes.

For functions, in function declarations, the keyword is indeed redundant. Function declarations do not have an implicit definition.

南渊 2024-07-26 07:06:55

extern 关键字根据环境采用不同的形式。 如果声明可用,extern 关键字将采用先前在翻译单元中指定的链接。 如果没有任何此类声明,extern 指定外部链接。

static int g();
extern int g(); /* g has internal linkage */

extern int j(); /* j has tentative external linkage */

extern int h();
static int h(); /* error */

以下是 C99 草案 (n1256) 中的相关段落:

6.2.2 标识符的链接

[...]

4 对于在该标识符的先前声明可见的范围内使用存储类说明符 extern 声明的标识符,23) 如果先前的声明指定了内部或
外部链接,后面声明时标识符的链接与
先前声明中指定的链接。 如果没有可见的事先声明,或者如果事先声明
声明指定没有链接,则该标识符具有外部链接。

5 如果函数的标识符声明没有存储类说明符,则其链接
的确定与使用存储类说明符 extern 声明时完全相同。 如果
对象标识符的声明具有文件范围并且没有存储类说明符,
它的链接是外部的。

The extern keyword takes on different forms depending on the environment. If a declaration is available, the extern keyword takes the linkage as that specified earlier in the translation unit. In the absence of any such declaration, extern specifies external linkage.

static int g();
extern int g(); /* g has internal linkage */

extern int j(); /* j has tentative external linkage */

extern int h();
static int h(); /* error */

Here are the relevant paragraphs from the C99 draft (n1256):

6.2.2 Linkages of identifiers

[...]

4 For an identifier declared with the storage-class specifier extern in a scope in which a prior declaration of that identifier is visible,23) if the prior declaration specifies internal or
external linkage, the linkage of the identifier at the later declaration is the same as the
linkage specified at the prior declaration. If no prior declaration is visible, or if the prior
declaration specifies no linkage, then the identifier has external linkage.

5 If the declaration of an identifier for a function has no storage-class specifier, its linkage
is determined exactly as if it were declared with the storage-class specifier extern. If
the declaration of an identifier for an object has file scope and no storage-class specifier,
its linkage is external.

虐人心 2024-07-26 07:06:55

内联函数对于 extern特殊规则代码> 的意思。 (请注意,内联函数是 C99 或 GNU 扩展;原始 C 中没有它们。

对于非内联函数,不需要 extern,因为默认情况下它是打开的。

请注意,C++ 的规则例如,您要从 C++ 调用的 C 函数的 C++ 声明中需要 extern "C",并且关于 inline 有不同的规则。

Inline functions have special rules about what extern means. (Note that inline functions are a C99 or GNU extension; they weren't in original C.

For non-inline functions, extern is not needed as it is on by default.

Note that the rules for C++ are different. For example, extern "C" is needed on the C++ declaration of C functions that you are going to call from C++, and there are different rules about inline.

请爱~陌生人 2024-07-26 07:06:55

IOW,extern 是多余的,并且什么也不做。

这就是为什么 10 年后:

请参阅提交 ad6dad0提交 b199d71, 提交 5545442(2019 年 4 月 29 日)作者:Denton Liu (Denton-L)
(由 Junio C Hamano -- gitster -- 合并于 提交 4aeeef3,2019 年 5 月 13 日)

*.[ch]:使用 extern “noreferrer”>spatch

有人呼吁从函数声明中删除 extern

删除 Coccinelle 捕获的函数声明中的一些“extern”实例。
请注意,Coccinelle 在处理带有 __attribute__ 或 varargs 的函数时遇到一些困难,因此留下了一些 extern 声明,以便在将来的补丁中处理。

这是使用的 Coccinelle 补丁:

<前><代码>@@
T 型;
标识符f;
@@
- 外部
T f(...);

它的运行方式是:

 $ git ls-files \*.{c,h} | 
      grep -v ^兼容/ | 
      xargs spatch --sp-file contrib/coccinelle/noextern.cocci --in-place 
  

但这并不总是那么简单:

请参阅 commit 7027f50 (2019 年 9 月 4 日)作者:Denton Liu (Denton-L).
(由 Denton Liu -- Denton-L -- 合并提交 7027f50,2019 年 9 月 5 日)

compat/*.[ch]:使用 spatch 从函数声明中删除 extern

在 5545442 (*.[ch]) 中:使用以下命令从函数声明中删除 extern
spatch,2019-04-29,Git v2.22.0-rc0),我们使用 spatch 从函数声明中删除了外部文件,但我们有意排除 compat/ 下的文件,因为有些文件直接从上游复制,我们应该避免搅动它们,以便手动合并未来的更新会更简单。

在最后一次提交中,我们确定了从上游获取的文件
因此我们可以排除它们并对其余部分运行 spatch

这是使用的 Coccinelle 补丁:

<前><代码>@@
T 型;
标识符f;
@@
- 外部
T f(...);

它的运行方式是:

$ git ls-files compat/\*\*.{c,h} | 
      xargs spatch --sp-file contrib/coccinelle/noextern.cocci --in-place 
  $ git checkout -- \ 
      兼容/正则表达式/ \ 
      兼容/inet_ntop.c \ 
      兼容/inet_pton.c \ 
      兼容/nedmalloc/ \ 
      兼容/obstack.{c,h} \ 
      兼容/民意调查/ 
  

Coccinelle 在处理 __attribute__ 和 varargs 时遇到一些麻烦,因此
我们运行以下命令以确保没有留下任何剩余的更改
后面:

$ git ls-files compat/\*\*.{c,h} | 
      xargs sed -i'' -e 's/^\(\s*\)extern \([^(]*([^*]\)/\1\2/' 
  $ git checkout -- \ 
      兼容/正则表达式/ \ 
      兼容/inet_ntop.c \ 
      兼容/inet_pton.c \ 
      兼容/nedmalloc/ \ 
      兼容/obstack.{c,h} \ 
      兼容/民意调查/ 
  

请注意,在 Git 2.24(2019 年第 4 季度)中,任何虚假的 extern 都会被删除。

请参阅 提交 65904b8(2019 年 9 月 30 日),作者:艾米丽·谢弗 (nasamuffin)
帮助者:Jeff King (peff)
请参阅 提交 8464f94(2019 年 9 月 21 日),作者:刘丹顿 (Denton-L)
帮助者:Jeff King (peff)
(由 Junio C Hamano -- gitster -- 合并于 提交 59b19bc,2019 年 10 月 7 日)

promisor-remote.h:从函数声明中删除extern

在创建此文件的过程中,每次引入新函数声明时,它都会包含一个 extern
但是,从 5545442 (*.[ch]) 开始:使用 spatch 从函数声明中删除 extern,2019-04-29,Git v2.22.0-rc0),我们一直在积极尝试阻止在函数中使用 extern声明,因为它们是不必要的。

删除这些虚假的extern

IOW, extern is redundant, and does nothing.

That is why, 10 years later:

See commit ad6dad0, commit b199d71, commit 5545442 (29 Apr 2019) by Denton Liu (Denton-L).
(Merged by Junio C Hamano -- gitster -- in commit 4aeeef3, 13 May 2019)

*.[ch]: remove extern from function declarations using spatch

There has been a push to remove extern from function declarations.

Remove some instances of "extern" for function declarations which are caught by Coccinelle.
Note that Coccinelle has some difficulty with processing functions with __attribute__ or varargs so some extern declarations are left behind to be dealt with in a future patch.

This was the Coccinelle patch used:

  @@
    type T;
    identifier f;
    @@
    - extern
    T f(...);

and it was run with:

  $ git ls-files \*.{c,h} |
    grep -v ^compat/ |
    xargs spatch --sp-file contrib/coccinelle/noextern.cocci --in-place

This is not always straightforward though:

See commit 7027f50 (04 Sep 2019) by Denton Liu (Denton-L).
(Merged by Denton Liu -- Denton-L -- in commit 7027f50, 05 Sep 2019)

compat/*.[ch]: remove extern from function declarations using spatch

In 5545442 (*.[ch]: remove extern from function declarations using
spatch, 2019-04-29, Git v2.22.0-rc0), we removed externs from function declarations using spatch but we intentionally excluded files under compat/ since some are directly copied from an upstream and we should avoid churning them so that manually merging future updates will be simpler.

In the last commit, we determined the files which taken from an upstream
so we can exclude them and run spatch on the remainder.

This was the Coccinelle patch used:

@@
type T;
identifier f;
@@
- extern
  T f(...);

and it was run with:

$ git ls-files compat/\*\*.{c,h} |
    xargs spatch --sp-file contrib/coccinelle/noextern.cocci --in-place
$ git checkout -- \
    compat/regex/ \
    compat/inet_ntop.c \
    compat/inet_pton.c \
    compat/nedmalloc/ \
    compat/obstack.{c,h} \
    compat/poll/

Coccinelle has some trouble dealing with __attribute__ and varargs so
we ran the following to ensure that no remaining changes were left
behind:

$ git ls-files compat/\*\*.{c,h} |
    xargs sed -i'' -e 's/^\(\s*\)extern \([^(]*([^*]\)/\1\2/'
$ git checkout -- \
    compat/regex/ \
    compat/inet_ntop.c \
    compat/inet_pton.c \
    compat/nedmalloc/ \
    compat/obstack.{c,h} \
    compat/poll/

Note that with Git 2.24 (Q4 2019), any spurious extern is dropped.

See commit 65904b8 (30 Sep 2019) by Emily Shaffer (nasamuffin).
Helped-by: Jeff King (peff).
See commit 8464f94 (21 Sep 2019) by Denton Liu (Denton-L).
Helped-by: Jeff King (peff).
(Merged by Junio C Hamano -- gitster -- in commit 59b19bc, 07 Oct 2019)

promisor-remote.h: drop extern from function declaration

During the creation of this file, each time a new function declaration was introduced, it included an extern.
However, starting from 5545442 (*.[ch]: remove extern from function declarations using spatch, 2019-04-29, Git v2.22.0-rc0), we've been actively trying to prevent externs from being used in function declarations because they're unnecessary.

Remove these spurious externs.

别靠近我心 2024-07-26 07:06:55

extern 关键字通知编译器该函数或变量具有外部链接 - 换句话说,它在定义它的文件以外的文件中是可见的。 从这个意义上来说,它与static 关键字具有相反的含义。 在定义时放置 extern 有点奇怪,因为没有其他文件可以看到该定义(或者会导致多个定义)。 通常,您将 extern 放在具有外部可见性的声明中(例如头文件),并将定义放在其他地方。

The extern keyword informs the compiler that the function or variable has external linkage - in other words, that it is visible from files other than the one in which it is defined. In this sense it has the opposite meaning to the static keyword. It is a bit weird to put extern at the time of the definition, since no other files would have visibility of the definition (or it would result in multiple definitions). Normally you put extern in a declaration at some point with external visibility (such as a header file) and put the definition elsewhere.

微凉徒眸意 2024-07-26 07:06:55

声明一个函数 extern 意味着它的定义将在链接时解析,而不是在编译期间解析。

与未声明为 extern 的常规函数​​不同,它可以在任何源文件中定义(但不能在多个源文件中定义,否则您将收到链接器错误,指出您已给出该函数的多个定义),包括它被声明为 extern。因此,在您的情况下,链接器解析同一文件中的函数定义。

我认为这样做没有多大用处,但是进行此类实验可以更好地了解该语言的编译器和链接器的工作原理。

declaring a function extern means that its definition will be resolved at the time of linking, not during compilation.

Unlike regular functions, which are not declared extern, it can be defined in any of the source files(but not in multiple source files otherwise you'll get linker error saying that you've given multiple definitions of the function) including the one in which it is declared extern.So, in ur case the linker resolves the function definition in the same file.

I don't think doing this would be much useful however doing such kind of experiments gives better insight about how the language's compiler and linker works.

我是有多爱你 2024-07-26 07:06:55

在 C 中,函数被隐式定义为 extern,无论是否实际声明了关键字。

因此,代码:

    int f() {return 0;}

编译器将视为

    extern int f() {return 0;}

本质上,典型的函数定义和前面带有 extern 关键字的函数定义之间没有语义差异,如本例所示。 您可以在 https://www. geeksforgeeks.org/understanding-extern-keyword-in-c/

In C, functions are implicitly defined as extern, regardless of whether or not the keyword is actually stated.

So, the code:

    int f() {return 0;}

The compiler will treat as

    extern int f() {return 0;}

Essentially, there is no semantic difference between a typical function definition and one preceded by the extern keyword, as in this example. You can read a more in depth explanation of this at https://www.geeksforgeeks.org/understanding-extern-keyword-in-c/

染墨丶若流云 2024-07-26 07:06:55

它不起作用的原因是因为在链接时链接器尝试解析 extern 定义(在您的情况下 extern int f())。 不管是在同一个文件还是不同的文件中找到,只要找到就行。

希望这能回答您的问题。

The reason it has no effect is because at the link-time the linker tries to resolve the extern definition (in your case extern int f()). It doesn't matter if it finds it in the same file or a different file, as long as it is found.

Hope this answers your question.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文