在C语言中,函数是否需要使用extern关键字?

17

在我看来,即使我没有使用extern声明,在另一个文件中引用一个函数,gcc也可以编译该单元。因此,我想知道函数是否需要在任何地方使用extern声明?我知道你需要为变量使用extern。


可能是C语言中extern关键字对函数的影响的重复内容。 - Ciro Santilli OurBigBook.com
4个回答

14

默认情况下,函数具有extern存储类别说明符(除非它们明确定义为静态)。

extern存储类别说明符

如果声明描述一个函数或者出现在函数之外并描述具有外部链接的对象,则关键字extern是可选的。如果您不指定存储类别说明符,则函数被认为具有外部链接。

....

如果在不带存储类别说明符的声明之前包含相同函数的声明并使用了静态存储类别说明符,由于这些声明不兼容,会导致错误。将extern存储类别说明符包含在原始声明中是有效的,函数将具有内部链接。


12

虽然不是必需的,但我更喜欢在标题中使用它,以加强这个函数是在其他地方定义的这个概念。

对我来说,这样做:

int func(int i);

这是一个函数的前向声明,稍后会用到,而这个:

extern int func(int i);

这是一个函数声明,它将在此处使用,但在其他地方定义。

这两行代码在功能上是相同的,但我使用 extern 关键字来记录它们之间的区别,并保持与普通变量的一致性(在普通变量中差异很重要,并具有确切的含义)。


2
我必须说,我觉得在头文件中这样做非常丑陋,因为它最终会使原型几乎肯定超过80个字符,导致破碎的行或水平滚动,两者都不是很愉快。 - R.. GitHub STOP HELPING ICE
@R.. - 有争议。它只占用了七个字符(包括空格),这相当少 - 它只能为您提供额外的const或最多一个参数的空间。这还取决于您认为有太多参数的数量。 - Chris Lutz
所以我们也可以这样断言,“extern 存在是为了向后兼容(并非兼容性)?” - Shamim Hafiz - MSFT
1
谢谢回答。但另一个问题是,即使我完全省略了声明(包括extern关键字和函数原型),它仍然可以被编译。 - Xiaolong Li
1
@Xiaolong Li - 如果类型不是“int”,并且不在C99中,则无法正确执行。 - Chris Lutz
显示剩余2条评论

6

你不一定需要使用extern关键字来声明变量。

C语言发明时,Unix链接器也随之诞生,并以不为人知但聪明的方式推进了技术的发展。其中一个贡献是将所有符号定义为小型“公共块”。这样就可以使用单个语法来声明,而无需指定哪个模块正在分配空间(只有一个模块实际上可以初始化该对象,但没有人需要这样做)。

实际上有三种情况需要考虑:

  1. 原型的前向声明(可选,因为遗留的C代码必须在没有它们的情况下编译)。

  2. 除了一个文件外,所有文件中非函数对象(变量)的外部声明(仅在具有低劣链接器的非Unix系统上需要。希望这些情况现在很少见)。

  3. 对于函数,如果没有函数体形成定义,则默认为外部声明。


1
主机上的链接器比微不足道的Unix ld先进得多。 "common"的概念出现在FORTRAN中。 - Jim Balter
1
我同意Fortran的观点,有些链接器具有许多功能。但是您不想使用它们,例如广泛的覆盖支持层次结构。在其中一个“高级”主机链接器上,有500个公共块的限制,但是ld(1)可以为每个符号执行一个公共块。 - DigitalRoss
有些人确实想要使用它们,这也是为什么它们存在的原因——但当我说那些链接器很先进时,并不是指叠加层。而且,在未经宣传但巧妙的方式中增加限制并不是艺术的进步。 - Jim Balter
好的,但是对符号的处理使得类型未定义但非零值的共同块大小能够上升。这个简单的方法意味着符号只需要几位类型和一个单一的值,但可以合并声明,决定数据和bss之间的关系,合并数据和bss,处理所有这些的本地和全局版本,并检测多重定义。使用相同的格式来进行输入和输出怎么样?如果那些大型机操作系统和链接器真的很好,为什么它们现在被埋得那么深,被人遗忘了呢? - DigitalRoss
1
为什么BetaMax被深埋和被遗忘了?我并没有谈论操作系统,但是关于Multics和Burroughs操作系统(用Algol编写)有许多值得称道的地方,但这些东西在意识形态者眼中已经失去了意义。 - Jim Balter
@jimBalter:请提到一些关于旧版 ld 链接器的技术优点。到目前为止,我已经提供了我的观点的技术例子,而你只是批评我对它的赞赏。我曾经编写过链接器,并在这里提出了技术论点。你只是抛泥并陈述意见。你能否用一些技术方面的东西提高一下自己的水平? - DigitalRoss

2
据我所记,按照标准,所有函数声明默认都被视为"extern",因此没有必要明确指定。但这并不意味着该关键字无用,因为它也可以与变量一起使用(在这种情况下,它是解决链接问题的唯一解决方案)。但对于函数而言,是可选的。
稍微详细一点的回答是,它允许您使用在另一个源代码文件中编译的变量,但不会为该变量保留内存。因此,要使用 extern,您必须拥有一个源代码文件或库单元,在顶层(而不是在函数内部)包含变量的内存空间。现在,您可以通过在其他源代码文件中定义同名的 extern 变量来引用该变量。
通常应避免使用 extern 定义。它们很容易导致难以管理的代码和难以定位的错误。当然,也有一些例外情况,其他解决方案可能不切实际。例如,stdin 和 stdout 是映射到 stdin.h 中类型为 FILE* 的 extern 数组变量的宏;该数组的内存空间位于标准 C 库单元中。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接