【问题标题】:Why do I need to declare a global as `extern` if I want to use it?如果我想使用它,为什么需要将全局声明为“extern”?
【发布时间】:2019-10-04 23:39:27
【问题描述】:

我了解 C 中的文件范围变量默认为 extern - 即使没有明确指定(如对 Global variables in C are static or not? 的回答中所述)。

不过我也知道,如果a.h声明了extern int x = 10;,而b.c想访问x,则需要声明为extern

为什么会这样?为什么不直接访问x 而无需任何额外的extern 内容?这种机制的技术解释是什么?

【问题讨论】:

  • 我知道 C 中的文件范围变量默认是外部变量 - 不,它们不是。他们可以在其他翻译单元中为externed,除非他们是statich 文件与它有一点关系,因为它们只是以文本方式粘贴到源代码中。如果你有extern int x,它和你把它放在包含c文件中的一样。
  • 请注意,当设置为 fussy(使用 -Werror)时,GCC (9.1.0) 会说:ext59.c:1:12: error: ‘x’ initialized and declared ‘extern’ [-Werror] 当被要求编译 extern int x = 10; — 你不应该将 extern 与初始化程序结合使用. (没有-Werror,它只是一个警告,即使没有警告请求也会出现。)

标签: c variables syntax


【解决方案1】:

因为变量在文件外部;它是在另一个文件中定义的,您只需告诉该文件它存在,但它不会在那里找到它。那么链接器的工作就是解决这个问题。

例子:

// a.c
int x = 7;

a.c 定义了变量

// a.h
extern int x;

a.h 知道变量存在,但不知道在哪里。任何包含 a.h 的文件都将获得该知识

// b.c
#include "a.h"

b.c,因为它包含了 a.h,现在知道变量 x 存在,但不知道在哪里。

链接器将解析同一个变量 x 的不同用法。最好是一样的,不然会出问题的。

你可以骗啊,写一个float而不是int,只有链接器可能会注意到,因为编译器实际上对ac一无所知(嗯,ac应该包含ah,所以它会注意到, 但如果你不包含它,你可能会撒谎)

在整个项目中,每个变量必须有一个且只有一个非外部定义。 0或2或更多,链接器会报错。

【讨论】:

    【解决方案2】:

    全局变量的存储空间只分配一次。例如在main.c:

    int gMyCounter;
    

    在您想要使用或访问此变量的任何其他模块(C 文件)中,您必须告诉编译器它的存在及其类型。您可以使用 extern 关键字来执行此操作,例如在 mymodule.c 中:

    extern int gMyCounter;
    

    在链接期间,链接器看到 mymodule.o 需要变量 gMyCounter 并在其他 .o 文件和库中搜索它。它在main.o 中找到它。

    【讨论】:

    • 所以如果我理解正确的话——用extern 声明一个变量意味着“这个变量是在其他地方定义的”。而它定义的地方不需要需要说extern?
    • @AvivCohn For gMyCounter 在文件范围内声明并具有外部链接: (1) extern int gMyCounter;(无初始化程序)表示它在其他地方定义; (2) int gMyCounter;(无初始化器)表示这里暂时定义,但可能在别处完全定义; (3) extern int gMyCounter = 0;int gMyCounter = 0; (带初始化器)都意味着它在这里完全定义。如果有初始化器,extern 关键字是多余的。
    • @IanAbbott,所以mymodule.c 中的extern int gMyCounter=1;再次为其分配存储空间吗? (因为 main.c 中的 extern int gMyCounter; 也会分配存储空间?)。这会导致链接器抱怨多重定义的符号吗?
    • @PaulOgilvie extern int gMyCounter; 不分配存储空间,但 extern int gMyCounter=1; 会。如果在超过 1 个翻译单元中定义了一个变量,则链接器将抱怨,除非系统将翻译单元的未解析、暂定定义的变量(将自动初始化为 0)放在 BSS(块共享存储)部分并折叠多个将同一未解析的、暂定定义的变量从不同的翻译单元定义为单个变量。这超出了 C 标准的范围,但很常见,继承自 Fortran。
    • @IanAbbott:通常会解析多个暂定定义和一个或零个正常定义,但多个正常定义会导致错误。
    【解决方案3】:

    头文件a.h不应包含

    extern int x = 10;
    

    只是

    extern int x;
    

    那么文件a.c应该定义

    int x = 10;
    

    当文件b.c 想要使用x 时,它应该很简单

    #include "a.h"
    

    编译器在编译b.c 时会知道x 是在其他地方定义的,链接器会找到引用并修复它。


    从添加的链接编辑中:如果定义位于编译器看不到的编译单元中,则该变量不会神奇地变为可用:您需要将其声明为 extern 编译器可以看到的地方,所以它知道它将可用。它与函数原型的目的有些相同:它是一种声明,而不是定义。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-06-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-22
      相关资源
      最近更新 更多