如何在C中正确使用extern关键字
我的问题是关于什么时候应该用C中的extern
关键字引用一个函数。
我没有看到什么时候应该在实践中使用。 在编写程序时,我使用的所有功能都可以通过我已经包含的头文件提供。 那么为什么extern
能够访问头文件中没有公开的东西呢?
我可能会考虑extern
如何不正确地工作,如果是的话,请纠正我。
编辑:如果你是extern
东西,当它是默认的声明没有关键字在头文件?
“ extern
”改变了联系。 使用关键字时,假定函数/变量在其他地方可用,解析延迟到链接器。
函数和变量之间的“extern”是有区别的:在变量上它没有实例化变量本身,即不分配任何内存。 这需要在别的地方完成。 因此,如果要从其他地方导入变量,这一点很重要。 对于函数,这只会告诉编译器,连接是extern。 因为这是默认的(你使用关键字“static”来表示一个函数没有使用extern链接进行绑定),所以你不需要明确地使用它。
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解决了实现。
已经说过, extern
关键字在功能上是多余的。
至于在编译单元之间共享的变量,你应该用extern关键字在头文件中声明它们,然后在一个源文件中定义它们,而不用extern关键字。 最佳实践是单个源文件应该是共享头文件名的文件。
在C语言中,“extern”对于函数原型是隐含的,因为原型声明了一个在其他地方定义的函数。 换句话说,一个函数原型默认有外部链接; 使用'extern'是好的,但是是多余的。
(如果需要静态链接,则该函数在其原型和函数头中都必须声明为“静态”,并且通常都应该在同一个.c文件中)。
关于extern
关键字的一篇非常好的文章,以及示例: http : //www.geeksforgeeks.org/understanding-extern-keyword-in-c/
虽然我不同意在函数声明中使用extern
是多余的。 这应该是一个编译器设置。 所以我建议在需要的时候在函数声明中使用extern
。
多年以后,我发现了这个问题。 阅读完所有的答案和评论后,我可以澄清一些细节…对于通过眼镜搜索来到这里的人来说,这可能是有用的。
这个问题具体是关于使用“extern”函数,所以我将忽略使用全局变量的“extern”。
我们来定义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;
为了编译和链接,我们必须在我们调用该函数的同一个源代码文件中定义“function_2”。 其他两个函数可以用不同的源代码“ .C”来定义, 或者它们可以位于任何二进制文件( .OBJ,* .LIB,* .DLL)中,对此我们可能没有源代码。
让我们再次在不同的“* .C”文件中包含头文件“my_project.H”,以更好地理解它们之间的区别。 在同一个项目中,我们添加以下文件// ————————————–
//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;
需要注意的重要特性:当头文件中的函数被定义为“静态”时,编译器/链接器必须在每个使用该包含文件的模块中找到具有该名称的函数实例。
作为C库一部分的函数只能在一个模块中被替换,只需在该模块中用“静态”重新定义原型即可。 例如,将任何调用替换为“malloc”和“free”以添加内存泄漏检测功能。
说明符“extern”并不是真正需要的函数。 当“静态”没有找到时,一个函数总是被假定为“extern”。
但是,“extern”不是变量的默认值。 通常情况下,定义变量在许多模块中可见的任何头文件都需要使用“extern”。 唯一的例外是,如果一个头文件被保证只包含一个模块。
许多项目经理会要求将这样的变量放在模块的开头,而不是放在任何头文件中。 一些大型项目,比如电子游戏模拟器“Mame”,甚至要求这样的变量只出现在使用它们的第一个函数之上。
如果程序中的每个文件首先被编译成一个目标文件,那么这个目标文件被链接在一起,你需要extern
。 它告诉编译器:“这个函数存在,但是它的代码是在别的地方,不要惊慌。
头文件中的函数和变量的所有声明都应该是extern
。
这个规则的例外是在头文件中定义的内联函数,而在头文件中定义的变量必须是翻译单元(包含头文件的源文件)的本地代码:它们应该是static
。
在源文件中, extern
不应该用于文件中定义的函数和变量。 只需将static
前缀定义为本地定义,并且对共享定义不做任何操作 – 默认情况下它们将是外部符号
在源文件中完全使用extern
的唯一原因是声明在其他源文件中定义的函数和变量,并且没有提供头文件。
声明函数原型extern
实际上是不必要的。 有些人不喜欢它,因为它会浪费空间和功能声明已经有一个倾向于溢出线限制。 其他人喜欢它,因为这样,函数和变量可以用同样的方式处理。
当你在不同的dll或lib上定义了这个函数时,编译器会根据链接器找到它。 典型情况是当您从OS API调用函数时。
其他源文件中实际定义的函数只能在头文件中声明 。 在这种情况下,在标题中声明原型时,应该使用extern 。
大多数情况下,你的功能将是下面的一个(更像是一个最佳实践):
- 静态(在.c文件外部不可见的普通函数)
- 静态内联(从.c或.h文件内联)
- extern(声明在下一种标题(见下文))
- [没有任何关键字](正常的函数意味着使用extern声明来访问)
Extern:“extern关键字在默认情况下是在任何变量声明之前出现的,编译器会隐藏它,当你想声明一个函数并在项目文件的某个地方定义它时,extern在这种情况下会起到很好的作用。一个套接字程序,你正在使用#include,如果你是一个Linux用户,在你的机器上找到一个文件socket.h并打开它,你可以在很多的extern函数声明中,这意味着你的头文件“socket.h”声明所有需要的函数,当你开始编译的时候会得到负载,那些extern函数是在socket二进制文件中定义的,你只需要包含头文件,加载二进制文件是链接器的责任。
以上所有例子都讲述了这个用法,我只是把它扩展到OS级别。