首页 文章

extern关键字对C函数的影响

提问于
浏览
157

在C中,我没有注意到函数声明之前使用的 extern 关键字的任何影响 . 起初,我认为在单个文件 forces 中定义 extern int f(); 时,您要在文件范围之外实现它 . 但是我发现两者都是:

extern int f();
int f() {return 0;}

extern int f() {return 0;}

编译得很好,没有gcc的警告 . 我用 gcc -Wall -ansi ;它甚至不接受 // 评论 .

使用 extern before function definitions 有什么影响吗?或者它只是一个可选的关键字,没有功能的副作用 .

在后一种情况下,我不明白为什么标准设计师选择用多余的关键词来乱丢语法 .

EDIT: 为了澄清,我知道变量中有 extern 的用法,但我只是在 functions 中询问 extern .

8 回答

  • 20

    我们有两个文件,foo.c和bar.c.

    这是foo.c

    #include <stdio.h>
    
    volatile unsigned int stop_now = 0;
    extern void bar_function(void);
    
    int main(void)
    {
      while (1) {
         bar_function();
         stop_now = 1;
      }
      return 0;
    }
    

    现在,这是bar.c

    #include <stdio.h>
    
    extern volatile unsigned int stop_now;
    
    void bar_function(void)
    {
       while (! stop_now) {
          printf("Hello, world!\n");
          sleep(30);
       }
    }
    

    正如您所看到的,我们在foo.c和bar.c之间没有共享头,但是bar.c需要在链接时在foo.c中声明的内容,而foo.c在链接时需要bar.c中的函数 .

    By using 'extern', you are telling the compiler that whatever follows it will be found (non-static) at link time; don't reserve anything for it in the current pass since it will be encountered later. Functions and variables are treated equally in this regard.

    如果您需要在模块之间共享一些全局并且不想在标头中放置/初始化它,这非常有用 .

    从技术上讲,库公共头中的每个函数都是“extern”,但是根据编译器的不同,将它们标记为几乎没有任何好处 . 大多数编译器都可以自己解决这个问题 . 如您所见,这些功能实际上是在其他地方定义的 .

    在上面的例子中,main()只会打印一次hello world,但会继续输入bar_function() . 另请注意,bar_function()在此示例中不会返回(因为它只是一个简单的示例) . 想象一下,当信号被服务(因此,易失性)时,stop_now被修改,如果这看起来不够实用 .

    Externs对于信号处理程序,你不想放在 Headers 或结构中的互斥体等非常有用 . 大多数编译器都会进行优化以确保它们不为外部对象保留任何内存,因为它们知道它们将在定义对象的模块中保留它 . 然而,在为公共功能进行原型设计时,用现代编译器指定它并没有什么意义 .

    希望有帮助:)

  • 74

    据我记得标准,默认情况下所有函数声明都被视为“extern”,因此不需要明确指定它 .

    这不会使这个关键字无用,因为它也可以与变量一起使用(在这种情况下 - 它是解决链接问题的唯一解决方案) . 但是有了这些功能 - 是的,它是可选的 .

  • 1

    您需要区分两个独立的概念:函数定义和符号声明 . “extern”是一个链接修饰符,向编译器提示有关后面引用的符号定义的位置(提示是“不在这里”) .

    如果我写

    extern int i;
    

    在C文件中的文件范围(在功能块之外),然后你说“变量可能在别处定义” .

    extern int f() {return 0;}
    

    既是函数f的声明,也是函数f的定义 . 在这种情况下,定义超越了extern .

    extern int f();
    int f() {return 0;}
    

    首先是声明,然后是定义 .

    如果要声明并同时定义文件范围变量,则使用 extern 是错误的 . 例如,

    extern int i = 4;
    

    将给出错误或警告,具体取决于编译器 .

    如果您明确要避免定义变量,则使用 extern 非常有用 .

    让我解释:

    假设文件a.c包含:

    #include "a.h"
    
    int i = 2;
    
    int f() { i++; return i;}
    

    文件a.h包括:

    extern int i;
    int f(void);
    

    和文件b.c包含:

    #include <stdio.h>
    #include "a.h"
    
    int main(void){
        printf("%d\n", f());
        return 0;
    }
    

    标头中的extern很有用,因为它在链接阶段"this is a declaration, and not a definition"告诉编译器 . 如果我删除定义了i的a.c中的行,为它分配空间并为其赋值,程序将无法使用未定义的引用进行编译 . 这告诉开发人员他已经引用了一个变量,但还没有定义它 . 另一方面,如果省略"extern"关键字,并删除 int i = 2 行,程序仍然编译 - 我将使用默认值0定义 .

    如果没有为它们显式赋值,则隐式定义文件范围变量,默认值为0或NULL - 与您在函数顶部声明的块范围变量不同 . extern关键字避免了这种隐式定义,因此有助于避免错误 .

    对于函数,在函数声明中,关键字是确实多余 . 函数声明没有隐式定义 .

  • 3

    extern 关键字采用不同的形式,具体取决于环境 . 如果声明可用, extern 关键字将采用前面在转换单元中指定的链接 . 在没有任何此类声明的情况下, extern 指定外部链接 .

    static int g();
    extern int g(); /* g has internal linkage */
    
    extern int j(); /* j has tentative external linkage */
    
    extern int h();
    static int h(); /* error */
    

    以下是C99草案(n1256)的相关段落:

    6.2.2标识符的链接[...] 4对于在该标识符的先前声明可见的范围内使用存储类说明符extern声明的标识符,23)如果先前声明指定内部或外部链接,后面声明中标识符的链接与先前声明中指定的链接相同 . 如果没有先前声明可见,或者先前声明未指定链接,则标识符具有外部链接 . 5如果函数的标识符声明没有存储类说明符,则确定其链接与使用存储类说明符extern声明的完全相同 . 如果对象的标识符声明具有文件范围而没有存储类说明符,则其链接是外部的 .

  • 7

    内联函数有special rules关于 extern 的含义 . (请注意,内联函数是C99或GNU扩展;它们不是原始C .

    对于非内联函数,默认情况下不需要 extern ,因为它处于启用状态 .

    请注意,C的规则是不同的 . 例如,在您要从C调用的C函数的C声明中需要 extern "C" ,并且有关于 inline 的不同规则 .

  • 127

    extern 关键字通知编译器函数或变量具有外部链接 - 换句话说,它可以从定义它的文件以外的文件中看到 . 从这个意义上讲,它与 static 关键字具有相反的含义 . 在定义时放置 extern 有点奇怪,因为没有其他文件可以看到定义(或者它会导致多个定义) . 通常,您将 extern 放在具有外部可见性的某个声明(例如头文件)中,并将定义放在其他位置 .

  • 14

    声明函数extern意味着它的定义将在链接时解析,而不是在编译期间解析 .

    与未声明为extern的常规函数不同,它可以在任何源文件中定义(但不能在多个源文件中定义,否则会出现链接器错误,表明您已经给出了函数的多个定义),包括它被声明为extern.So,在你的情况下,链接器解析同一文件中的函数定义 .

    我不认为这样做会有用,但是进行这样的实验可以更好地了解语言的编译器和链接器的工作原理 .

  • 2

    它没有效果的原因是因为在链接时链接器尝试解析extern定义(在你的情况下 extern int f() ) . 只要找到它,它是否在同一个文件或不同的文件中找到它并不重要 .

    希望这能回答你的问题 .

相关问题