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

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

and

extern int f() {return 0;}

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

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

在后一种情况下,我不明白为什么标准设计师选择用多余的关键字乱扔语法。

编辑:为了澄清,我知道在变量中有extern的用法,但我只问函数中的extern。


当前回答

IOW, extern是多余的,什么都不做。

这就是为什么十年后:

像Coccinelle这样的代码转换工具倾向于在函数声明中标记extern以便删除; 像git/git这样的代码库遵循这个结论,并从其代码中删除extern(适用于git 2.22, 2019年第二季度)。

参见刘丹顿(Denton- l)的commit ad6dad0, commit b199d71, commit 5545442(2019年4月29日)。 (由Junio C Hamano - gitster -在commit 4aeeef3中合并,2019年5月13日)

*.[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


但这并不总是那么简单:

参见刘丹顿(Denton- l)提交的7027f50(2019年9月04日)。 (由Denton Liu—Denton- l—在2019年9月5日提交7027f50中合并)

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/


请注意,在Git 2.24(2019年Q4)中,任何虚假的extern都被删除了。

参见Emily Shaffer (nasamuffin)的commit 65904b8(2019年9月30日)。 帮助:Jeff King (peff)。 参见刘丹顿(Denton- l)提交的8464f94(2019年9月21日)。 帮助:Jeff King (peff)。 (由Junio C Hamano—gitster—在commit 59b19bc中合并,2019年10月7日)

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

其他回答

在C语言中,函数隐式定义为extern,而不管是否实际声明关键字。

那么,代码:

    int f() {return 0;}

编译器将处理为

    extern int f() {return 0;}

从本质上讲,典型的函数定义与前面带有extern关键字的函数定义之间没有语义上的区别,如本例中所示。你可以在https://www.geeksforgeeks.org/understanding-extern-keyword-in-c/上阅读更深入的解释

内联函数对于extern的含义有特殊的规则。(注意,内联函数是C99或GNU扩展;他们不是原始的C。

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

注意,c++的规则是不同的。例如,在要从c++调用的C函数的c++声明上需要extern“C”,关于内联有不同的规则。

IOW, extern是多余的,什么都不做。

这就是为什么十年后:

像Coccinelle这样的代码转换工具倾向于在函数声明中标记extern以便删除; 像git/git这样的代码库遵循这个结论,并从其代码中删除extern(适用于git 2.22, 2019年第二季度)。

参见刘丹顿(Denton- l)的commit ad6dad0, commit b199d71, commit 5545442(2019年4月29日)。 (由Junio C Hamano - gitster -在commit 4aeeef3中合并,2019年5月13日)

*.[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


但这并不总是那么简单:

参见刘丹顿(Denton- l)提交的7027f50(2019年9月04日)。 (由Denton Liu—Denton- l—在2019年9月5日提交7027f50中合并)

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/


请注意,在Git 2.24(2019年Q4)中,任何虚假的extern都被删除了。

参见Emily Shaffer (nasamuffin)的commit 65904b8(2019年9月30日)。 帮助:Jeff King (peff)。 参见刘丹顿(Denton- l)提交的8464f94(2019年9月21日)。 帮助:Jeff King (peff)。 (由Junio C Hamano—gitster—在commit 59b19bc中合并,2019年10月7日)

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

我们有两个文件,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在信号被服务时被修改(因此是volatile),如果这看起来不够实际的话。

对于信号处理程序、一个你不想放在头文件或结构中的互斥量等,extern非常有用。大多数编译器会优化以确保它们不为外部对象保留任何内存,因为它们知道它们将在定义对象的模块中保留内存。但是,同样,在创建公共函数原型时,使用现代编译器指定它没有什么意义。

它没有效果的原因是在链接时,链接器试图解析extern定义(在您的例子中是extern int f())。它是在同一个文件中找到它还是在不同的文件中找到它并不重要,只要找到了它。

希望这能回答你的问题。