我的问题是关于何时应该使用 C 中的 extern
关键字引用函数。
我看不到什么时候应该在实践中使用它。在我编写程序时,我使用的所有功能都可以通过我包含的头文件获得。那么,为什么 extern
可以访问未在头文件中公开的内容?
我可能在思考 extern
的工作方式不正确,如果是这样,请纠正我。
另外.. 当它是在头文件中没有关键字的默认声明时,您是否应该extern
某些东西?
extern
更改链接。使用关键字,函数/变量被假定在其他地方可用,解析被推迟到链接器。
函数和变量的 extern
之间存在差异。
对于变量,它不实例化变量本身,即不分配任何内存。这需要在其他地方完成。因此,如果您想从其他地方导入变量,这一点很重要。
对于函数,这只告诉编译器链接是外部的。由于这是默认设置(您使用关键字 static
来指示函数未使用外部链接绑定),因此您无需显式使用它。
extern
告诉编译器此数据在某处定义并将与链接器连接。
借助此处的回复并在此处与几个朋友交谈是使用 extern
的实际示例。
示例 1 - 显示一个陷阱:
stdio.h
:
int errno;
myCFile1.c
:
#include <stdio.h>
// Code using errno...
myCFile2.c
:
#include <stdio.h>
// Code using errno...
如果 myCFile1.o
和 myCFile2.o
已链接,则每个 c 文件都有单独的 errno
副本。这是一个问题,因为相同的 errno
应该在所有链接文件中都可用。
示例 2 - 修复。
stdio.h
:
extern int errno;
stdio.c
:
int errno;
myCFile1.c
:
#include <stdio.h>
// Code using errno...
myCFile2.c
:
#include <stdio.h>
// Code using errno...
现在,如果 myCFile1.o
和 MyCFile2.o
都由链接器链接,它们都将指向同一个 errno
。因此,用 extern
解决实现。
已经说过 extern
关键字对于函数是多余的。
对于跨编译单元共享的变量,您应该在头文件中使用 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
)中。
让我们再次将标头 my_project.H
包含在不同的 *.C
文件中以更好地理解差异。在同一个项目中,我们添加以下文件:
// --------------------------------------
// 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 并不是函数真正需要的。当未找到 static 时,始终假定函数为 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。
大多数时候,您的功能将是以下之一(更像是最佳实践):
静态(在该 .c 文件之外不可见的普通函数)
静态内联(来自 .c 或 .h 文件的内联)
extern (在下一种标题中的声明(见下文))
[没有任何关键字](普通函数意味着使用 extern 声明访问)
当您在不同的 dll 或 lib 上定义该函数时,编译器会根据链接器来查找它。典型情况是当您从 OS API 调用函数时。