如何使用extern在源文件之间共享变量?

时间:2009-09-16 14:08:41

标签: c global-variables extern

我知道C中的全局变量有时会有extern个关键字。什么是extern变量?宣言是什么样的?它的范围是什么?

这与跨源文件共享变量有关,但这是如何工作的?我在哪里使用extern

17 个答案:

答案 0 :(得分:1609)

答案 1 :(得分:114)

extern变量是在另一个翻译单元中定义的变量的声明(由于sbi用于校正)。这意味着变量的存储空间被分配在另一个文件中。

假设您有两个.c - 文件test1.ctest2.c。如果您在int test1_var;中定义全局变量test1.c并且想要在test2.c中访问此变量,则必须在extern int test1_var;中使用test2.c

完整样本:

$ cat test1.c 
int test1_var = 5;
$ cat test2.c
#include <stdio.h>

extern int test1_var;

int main(void) {
    printf("test1_var = %d\n", test1_var);
    return 0;
}
$ gcc test1.c test2.c -o test
$ ./test
test1_var = 5

答案 2 :(得分:36)

Extern是用于声明变量本身位于另一个翻译单元中的关键字。

因此,您可以决定在翻译单元中使用变量,然后从另一个变量中访问它,然后在第二个变量中将其声明为extern,并且链接器将解析该符号。

如果你没有将它声明为extern,你将获得两个名为相同但根本不相关的变量,以及变量的多个定义的错误。

答案 3 :(得分:25)

我喜欢将extern变量视为您对编译器的承诺。

当遇到extern时,编译器只能找到它的类型,而不是它“存在”的位置,因此它无法解析引用。

你告诉它,“相信我。在链接时,这个引用将是可解析的。”

答案 4 :(得分:18)

extern告诉编译器要相信这个变量的内存是在别处声明的,所以它不会尝试分配/检查内存。

因此,您可以编译一个引用extern的文件,但如果该内存未在某处声明,则无法链接。

对全局变量和库很有用,但很危险,因为链接器没有键入check。

答案 5 :(得分:15)

添加extern会将变量定义变为变量声明。请参阅this thread,了解声明和定义之间的区别。

答案 6 :(得分:11)

extern的正确解释是你告诉编译器。您告诉编译器,尽管现在不存在,但是声明的变量将以某种方式由链接器找到(通常在另一个对象(文件)中)。无论你是否有一些外部声明,链接器都将是找到所有东西并将其组合在一起的幸运者。

答案 7 :(得分:8)

在C文件中的变量中,例如example.c给出了本地范围。编译器期望变量在同一个文件example.c中有它的定义,当它找不到相同的时候,它会抛出一个错误。另一方面,一个函数默认具有全局范围。因此,您不必明确地向编译器提及“看起来很粗鲁......您可能会在这里找到此函数的定义”。对于包含包含其声明的文件的函数就足够了。(实际上称为头文件的文件)。    例如,考虑以下2个文件:
 example.c

#include<stdio.h>
extern int a;
main(){
       printf("The value of a is <%d>\n",a);
}

example1.c

int a = 5;

现在,当您将两个文件一起编译时,请使用以下命令:

步骤1)cc -o ex example.c example1.c 步骤2)./ ex

您将获得以下输出:a的值为&lt; 5&gt;

答案 8 :(得分:7)

extern关键字与变量一起用于将其标识为全局变量。

  

它还表示您可以使用使用extern声明的变量   任何文件中的关键字虽然在其他文件中声明/定义。

答案 9 :(得分:6)

GCC ELF Linux实施

main.c

#include <stdio.h>

int not_extern_int = 1;
extern int extern_int;

void main() {
    printf("%d\n", not_extern_int);
    printf("%d\n", extern_int);
}

编译和反编译:

gcc -c main.c
readelf -s main.o

输出包含:

Num:    Value          Size Type    Bind   Vis      Ndx Name
 9: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    3 not_extern_int
12: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND extern_int

System V ABI Update ELF spec“符号表”一章解释:

  

SHN_UNDEF此节表索引表示符号未定义。当链接编辑器将此目标文件与定义指示符号的另一个目标文件组合在一起时,该文件对该符号的引用将链接到实际定义。

这基本上是C标准赋予extern变量的行为。

从现在开始,链接器的工作就是创建最终程序,但extern信息已经从源代码中提取到目标文件中。

在GCC 4.8上测试。

C ++ 17内联变量

在C ++ 17中,您可能希望使用内联变量而不是外部变量,因为它们易于使用(可以在标题上定义一次)并且功能更强大(支持constexpr)。请参阅:What does 'const static' mean in C and C++?

答案 10 :(得分:5)

extern 允许程序的一个模块访问程序的另一个模块中声明的全局变量或函数。 您通常在头文件中声明了外部变量。

如果您不希望程序访问您的变量或函数,请使用static告诉编译器此变量或函数不能在此模块之外使用。

答案 11 :(得分:5)

extern仅表示变量在别处定义(例如,在另一个文件中)。

答案 12 :(得分:4)

首先,extern关键字不用于定义变量;而是用于声明变量。我可以说extern是一个存储类,而不是数据类型。

extern用于让其他C文件或外部组件知道此变量已在某处定义。示例:如果要构建库,则无需在库本身的某处强制定义全局变量。该库将直接编译,但在链接文件时,它会检查定义。

答案 13 :(得分:4)

                 declare | define   | initialize |
                ----------------------------------

extern int a;    yes          no           no
-------------
int a = 2019;    yes          yes          yes
-------------
int a;           yes          yes          no
-------------

声明不会分配内存(必须为内存分配定义变量),但是定义会。 这只是对extern关键字的另一种简单见解,因为其他答案确实很棒。

答案 14 :(得分:3)

使用了

extern,因此一个first.c文件可以完全访问另一个second.c文件中的全局参数。

extern可以在first.c文件或first.c包含的任何标题文件中声明。

答案 15 :(得分:1)

使用xc8时,您必须小心声明变量 与每个文件中的相同类型错误地 在一个文件中声明int,在另一个文件中声明char。 这可能导致变量损坏。

这个问题在15年前的微芯片论坛上得到了很好的解决     / *请参阅“ http:www.htsoft.com” /     / “ forum / all / showflat.php / Cat / 0 / Number / 18766 / an / 0 / page / 0#18766”

但是此链接似乎不再起作用...

因此,我将尽快尝试解释它; 制作一个名为global.h的文件。

在其中声明以下内容

#ifdef MAIN_C
#define GLOBAL
 /* #warning COMPILING MAIN.C */
#else
#define GLOBAL extern
#endif
GLOBAL unsigned char testing_mode; // example var used in several C files

现在在文件main.c中

#define MAIN_C 1
#include "global.h"
#undef MAIN_C

这意味着在main.c中,变量将被声明为unsigned char

现在在其他文件中,仅包括global.h, 将该文件声明为该文件的外部代码

extern unsigned char testing_mode;

但是它将正确声明为unsigned char

旧的论坛帖子可能对此进行了更清晰的解释。 但这是使用编译器时的真正潜力gotcha 允许您在一个文件中声明一个变量,然后在另一个文件中将其声明为另一个类型。与之相关的问题 如果您说在另一个文件中将testing_mode声明为int 它会认为这是一个16位var,并且会覆盖ram的其他部分,从而可能损坏另一个变量。难以调试!

答案 16 :(得分:0)

一个非常简短的解决方案,我用来允许头文件包含外部引用或对象的实际实现。实际包含对象的文件仅执行#define GLOBAL_FOO_IMPLEMENTATION。然后,当我向该文件添加新对象时,它也显示在该文件中,而无需我复制和粘贴定义。

我在多个文件中使用此模式。因此,为了使内容尽可能独立,我只在每个标头中重用了一个GLOBAL宏。我的标题看起来像这样:

//file foo_globals.h
#pragma once  
#include "foo.h"  //contains definition of foo

#ifdef GLOBAL  
#undef GLOBAL  
#endif  

#ifdef GLOBAL_FOO_IMPLEMENTATION  
#define GLOBAL  
#else  
#define GLOBAL extern  
#endif  

GLOBAL Foo foo1;  
GLOBAL Foo foo2;


//file main.cpp
#define GLOBAL_FOO_IMPLEMENTATION
#include "foo_globals.h"

//file uses_extern_foo.cpp
#include "foo_globals.h