我的问题是,何时应使用extern
C中的关键字引用函数。
我看不到何时应该在实践中使用它。在编写程序时,我使用的所有功能都可以通过包含的头文件使用。那么,为什么extern
访问头文件中未公开的内容很有用?
我可能正在考虑extern
工作方式不正确,如果是这样,请纠正我。
编辑:extern
当它是在头文件中没有关键字的默认声明时,您应该做些什么吗?
我的问题是,何时应使用extern
C中的关键字引用函数。
我看不到何时应该在实践中使用它。在编写程序时,我使用的所有功能都可以通过包含的头文件使用。那么,为什么extern
访问头文件中未公开的内容很有用?
我可能正在考虑extern
工作方式不正确,如果是这样,请纠正我。
编辑:extern
当它是在头文件中没有关键字的默认声明时,您应该做些什么吗?
Answers:
“ extern
”会更改链接。使用关键字,功能/变量被假定为在其他地方可用,并且解析被推迟到链接器。
函数和变量上的“外部”之间是有区别的:在变量上,它不会实例化变量本身,即不分配任何内存。这需要在其他地方完成。因此,如果要从其他位置导入变量,则很重要。对于函数,这仅告诉编译器链接是外部的。由于这是默认设置(您可以使用关键字“ static”来指示未使用外部链接绑定功能),因此无需显式使用它。
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解决实现。
-Wall
和后也没有给出错误或警告-pedantic
。为什么呢 如何 ?
已经有人说过,extern
关键字对于功能是多余的。
对于跨编译单元共享的变量,应在带有extern关键字的头文件中声明它们,然后在没有extern关键字的单个源文件中对其进行定义。最佳实践是,单个源文件应该是共享头文件名的文件。
许多年后,我发现了这个问题。阅读完所有答案和评论后,我想我可以澄清一些细节...这对于通过Google搜索到达此处的人们可能很有用。
问题特别是关于使用“ 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;
注意的重要功能:
当在头文件中将函数定义为“静态”时,编译器/链接器必须在使用该包含文件的每个模块中找到具有该名称的函数实例。
通过仅在该模块中用“ static”重新定义原型,可以仅在一个模块中替换C库中的功能。例如,替换对“ malloc”和“ free”的任何调用以添加内存泄漏检测功能。
函数实际上并不需要说明符“ extern”。当找不到“静态”时,始终假定函数为“外部”。
但是,“ extern”不是变量的默认值。通常,任何将变量定义为在许多模块中可见的头文件都需要使用“ extern”。唯一的例外是,如果保证一个文件中只有一个模块包含头文件。
然后,许多项目经理会要求将此类变量放置在模块的开头,而不是放在任何头文件中。一些大型项目,例如视频游戏模拟器“ Mame”,甚至要求此类变量仅出现在使用它们的第一个函数上方。
在C语言中,函数原型隐含“ extern”,因为原型声明了在其他地方定义的函数。换句话说,默认情况下,函数原型具有外部链接。使用'extern'可以,但是很多余。
(如果需要静态链接,则必须在函数的原型和函数头中都将其声明为“ static”,并且通常都应将它们都放在同一.c文件中)。
我写了一篇非常不错的关于extern
关键字的文章以及示例:http : //www.geeksforgeeks.org/understanding-extern-keyword-in-c/
尽管我不同意extern
在函数声明中使用是多余的。这应该是编译器设置。因此,我建议extern
在需要时在函数声明中使用。
如果首先将程序中的每个文件编译为一个目标文件,则需要将这些目标文件链接在一起extern
。它告诉编译器“此函数存在,但是它的代码在其他地方。不要惊慌。”
头文件中的所有函数和变量声明都应为extern
。
此规则的例外是标头中定义的内联函数和变量(尽管标头中定义了),但变量必须在转换单元(标头包含在其中的源文件)中位于本地:这些应该是static
。
在源文件中,extern
不应将其用于文件中定义的函数和变量。只需在本地定义前面加上前缀,static
对共享定义不做任何事情-默认情况下,它们将是外部符号。
extern
在源文件中完全使用的唯一原因是声明在其他源文件中定义并且没有提供头文件的函数和变量。
extern
实际上,不需要声明函数原型。有些人不喜欢它,因为它只会浪费空间,并且函数声明已经倾向于溢出行限制。其他人喜欢它,因为这样可以对函数和变量进行相同的处理。
extern
对于函数声明是可选的,但是我喜欢以相同的方式对待变量和函数-至少那是我能想到的最合理的事情,因为我不完全记得为什么我开始这样做;)
在其他源文件中实际定义的函数只能在标头中声明。在这种情况下,你应该使用的extern时,宣布在头中的原型。
大多数时候,您的功能将是以下功能之一(更像是一种最佳实践):
当您在另一个dll或lib上定义了该函数时,编译器将根据链接程序进行查找。典型的情况是从OS API调用函数时。