我的问题是关于何时应使用C中的extern
关键字引用函数。
我没有看到何时应该在实践中使用它。在我编写程序时,我使用的所有函数都通过我包含的头文件提供。那么为什么extern
能够访问未在头文件中公开的内容呢?
我可能会想到extern
如何正常工作,如果是,请纠正我。
编辑:如果在标题文件中没有关键字的默认声明,您应该extern
吗?
答案 0 :(得分:270)
“extern
”会更改链接。使用关键字,假定函数/变量在其他地方可用,并且解析将延迟到链接器。
函数和变量的“extern”之间存在差异:在变量上它不会实例化变量本身,即不分配任何内存。这需要在其他地方完成。因此,如果您想从其他地方导入变量,这很重要。对于函数,这只告诉编译器链接是extern。由于这是默认值(使用关键字“static”表示函数未使用extern链接绑定),因此您无需显式使用它。
答案 1 :(得分:185)
extern 告诉编译器此数据已在某处定义,并将与链接器连接。
在这里的回复帮助下,在这里与几位朋友交谈是使用 extern 的实际例子。
示例1 - 显示陷阱:
File stdio.h:
int errno;
/* other stuff...*/
myCFile1.c:
#include <stdio.h>
Code...
myCFile2.c:
#include <stdio.h>
Code...
如果链接了myCFile1.o和myCFile2.o,则每个c文件都有 errno 的单独副本。这是一个问题,因为所有链接文件中都应该提供相同的 errno 。
示例2 - 修复。
File stdio.h:
extern int errno;
/* other stuff...*/
File stdio.c
int errno;
myCFile1.c:
#include <stdio.h>
Code...
myCFile2.c:
#include <stdio.h>
Code...
现在,如果链接器链接了myCFile1.o和MyCFile2.o,它们将指向相同的 errno 。因此,使用 extern 来解决实现。
答案 2 :(得分:28)
已经声明extern
关键字对于函数来说是多余的。
对于在编译单元之间共享的变量,您应该在带有extern关键字的头文件中声明它们,然后在单个源文件中定义它们,而不使用extern关键字。单个源文件应该是共享头文件名称的文件,以便进行最佳实践。
答案 3 :(得分:14)
在C中,'extern'隐含在函数原型中,因为原型声明了一个在其他地方定义的函数。换句话说,函数原型默认具有外部链接;使用'extern'很好,但是多余。
(如果需要静态链接,则必须在其原型和函数头中将该函数声明为'static',并且这些函数通常都应位于相同的.c文件中。)
答案 4 :(得分:12)
多年以后,我发现了这个问题。在阅读了每个答案和评论之后,我想我可以澄清一些细节......这对于通过谷歌搜索来到这里的人来说非常有用。
问题是关于使用&#34; extern&#34;函数,所以我将忽略使用&#34; extern&#34;全局变量。
让我们定义3个函数原型
//--------------------------------------
//Filename: "my_project.H"
extern int function_1(void);
static int function_2(void);
int function_3(void);
主要源代码可以使用头文件,如下所示
//--------------------------------------
//Filename: "my_project.C"
#include "my_project.H"
void main(void){
int v1 = function_1();
int v2 = function_2();
int v3 = function_3();
}
int function_2(void) return 1234;
为了编译和链接,我们必须定义&#34; function_2&#34;在我们调用该函数的相同源代码文件中。另外两个函数可以在不同的源代码中定义&#34; .C&#34;或者它们可能位于任何二进制文件( .OBJ,* .LIB,* .DLL)中,我们可能没有源代码。
让我们再次包含标题&#34; my_project.H&#34;在另一个&#34; * .C&#34;文件,以更好地了解差异。 在同一个项目中,我们添加以下文件 // --------------------------------------
//Filename: "my_big_project_splitted.C"
#include "my_project.H"
void old_main_test(void){
int v1 = function_1();
int v2 = function_2();
int v3 = function_3();
}
int function_2(void) return 5678;
int function_1(void) return 12;
int function_3(void) return 34;
需要注意的重要功能: 当一个函数被定义为&#34; static&#34;在头文件中,编译器/链接器必须在每个使用该包含文件的模块中找到具有该名称的函数的实例。
作为C库一部分的功能只能通过用&#34;静态&#34;重新定义原型而在一个模块中替换。只在那个模块中例如,替换对&#34; malloc&#34;的任何调用。并且&#34;免费&#34;添加内存泄漏检测功能。
说明符&#34; extern&#34;功能并不是真正需要的。当&#34;静态&#34;找不到函数,函数总是假定为&#34; extern&#39;。
然而,&#34; extern&#34;不是变量的默认值。通常,任何定义要在多个模块中可见的变量的头文件都需要使用&#34; extern&#34;。唯一的例外是如果一个且只有一个模块保证包含头文件。
然后,许多项目经理会要求将此变量放在模块的开头,而不是放在任何头文件中。一些大型项目,如视频游戏模拟器&#34; Mame&#34;甚至要求这样的变量仅出现在使用它们的第一个函数之上。
答案 5 :(得分:8)
关于extern
关键字的一篇非常好的文章,以及示例:http://www.geeksforgeeks.org/understanding-extern-keyword-in-c/
虽然我不同意在函数声明中使用extern
是多余的。这应该是一个编译器设置。因此,我建议在需要时在函数声明中使用extern
。
答案 6 :(得分:5)
如果程序中的每个文件首次编译为目标文件,则目标文件链接在一起,您需要extern
。它告诉编译器“这个函数存在,但它的代码在其他地方。不要惊慌。”
答案 7 :(得分:4)
头文件中函数和变量的所有声明都应该是extern
。
此规则的例外情况是标题和变量中定义的内联函数 - 尽管在标题中定义 - 必须是转换单元的本地函数(标题包含在其中的源文件):这些应该是{{1 }}
在源文件中,static
不应用于文件中定义的函数和变量。只需使用extern
为本地定义添加前缀,而不对共享定义执行任何操作 - 默认情况下它们将是外部符号。
在源文件中根本使用static
的唯一原因是声明在其他源文件中定义的函数和变量,并且没有提供头文件。
声明函数原型extern
实际上是不必要的。有些人不喜欢它,因为它只会浪费空间和功能声明已经有溢出线限制的趋势。其他人喜欢它,因为这样,函数和变量可以用同样的方式处理。
答案 8 :(得分:2)
如果在不同的dll或lib上定义了该函数,那么编译器会根据链接器来查找它。典型情况是从OS API调用函数。
答案 9 :(得分:2)
其他源文件中实际定义的函数只应在标题中声明。在这种情况下,当在标题中声明原型时,您应该使用 extern 。
大多数情况下,您的功能将是以下之一(更像是最佳做法):