【问题标题】:Is extern keyword for function necessary at all in C?在 C 中,函数的 extern 关键字是否完全必要?
【发布时间】:2011-03-10 04:39:22
【问题描述】:

在我看来,即使我在没有 extern 声明的情况下引用另一个文件中的函数,gcc 仍然可以编译该单元。所以我想知道在函数的任何地方是否都需要 extern 声明?我知道你需要 extern 作为变量。

【问题讨论】:

标签: c extern


【解决方案1】:

函数默认具有外部存储类说明符(除非它们明确定义为静态)

extern Storage Class Specifier

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

....

由于声明不兼容,在没有存储类说明符的声明之前包含具有存储类说明符 static 的同一函数的声明是错误的。在原始声明中包含 extern 存储类说明符是有效的,并且函数具有内部链接。

【讨论】:

    【解决方案2】:

    这不是必需的,但我更喜欢在标题中使用它来强化这个函数在其他地方定义的想法。

    对我来说,这是:

    int func(int i);
    

    是稍后需要的函数的前向声明,而这个:

    extern int func(int i);
    

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

    这两行在功能上是相同的,但我使用extern 关键字来记录差异,并与常规变量保持一致(其中差异很重要,并且完全具有该含义)。

    【讨论】:

    • 我必须说我发现它在标题中非常难看,因为它最终使原型几乎肯定超过 80 个字符,导致断线或水平滚动,这都不令人愉快。
    • @R.. - 值得商榷。它只占用七个字符(包括空格),这是相当少的 - 它只会为您提供额外的空间 const 或最多一个参数。这还取决于您认为“太多”的参数数量。
    • 所以我们也可以做出断言,“extern 是为了向后方便(不意味着兼容性)?
    • 感谢您的回答。但另一个问题是,即使我完全省略了声明(包括 extern 关键字和函数原型),它仍然可以编译。
    • @Xiaolong Li - 如果类型不是ints,而不是在 C99 中,则不正确。
    【解决方案3】:

    对于变量,您不一定需要extern

    当 C 被发明时,也编写了 Unix 链接器,它们以不为人知但聪明的方式推进了艺术。一项贡献是将所有符号定义为小的“公共块”。这允许声明的单一语法,而无需指定哪个模块正在分配空间。 (实际上只有一个模块可以初始化对象,但不需要任何人。)

    确实有三个考虑因素。

    1. 原型的前向声明。 (可选,因为旧版 C 必须在没有它们的情况下进行编译。)

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

    3. 对于函数,如果不存在函数体来形成定义,则 extern 已经是假设。

    【讨论】:

    • 大型机上的链接器比极小的 Unix ld 先进得多。 FORTRAN 中有“共同”的概念。
    • 我同意 Fortran,一些链接器有很多特性。但是您不想使用它们,例如覆盖支持的广泛层次结构。在其中一个“高级”大型机链接器上,有 500 个公共块的限制,但ld(1) 可以为每个符号做一个。
    • 有些人确实想使用它们,这就是它们存在的原因——但是当我说这些链接器是高级的时,我并没有想到覆盖。增加限制并不是一种不为人知的艺术进步,而是一种巧妙的方式。
    • 好的,但正是将具有未定义但非零值类型的符号处理为值大小的公共块,从而允许限制上升。一件简单的事情意味着符号只需要几位类型和一个值,但可以合并声明,在数据和 bss 之间做出决定,合并数据和 bss,处理所有这些的本地和全局版本,并检测多个定义。输入和输出使用相同的格式怎么样?如果那些大型机操作系统和链接器有什么好处,为什么它们现在被埋得如此之深,被遗忘了?
    • 为什么BetaMax现在被埋得如此深,被遗忘了?我对操作系统只字未提,但关于 Multics 和 Burroughs 操作系统(用 Algol 编写)可以说很多好话,但理论家们忽略了这些东西。
    【解决方案4】:

    据我所记得的标准,默认情况下所有函数声明都被视为“外部”,因此无需明确指定。 这不会使这个关键字无用,因为它也可以与变量一起使用(在这种情况下 - 它是解决链接问题的唯一解决方案)。但有了这些功能 - 是的,它是可选的

    更详细一点的答案是,它允许您使用在另一个源代码文件中编译的变量,但不为变量保留内存。因此,要使用 extern,您必须有一个源代码文件或一个库单元,其中包含顶层变量(而不是函数内)的内存空间。现在,您可以通过在其他源代码文件中定义同名的 extern 变量来引用该变量。

    一般来说,应该避免使用外部定义。它们很容易导致难以管理的代码和难以定位的错误。当然,也有其他解决方案不切实际的例子,但它们很少见。例如,stdin 和 stdout 是映射到 stdin.h 中 FILE* 类型的外部数组变量的宏;此数组的内存空间位于标准 C 库单元中。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-10-25
      • 2012-06-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多