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

我知道C中的全局variables有时候有extern关键字。 什么是externvariables? 什么是宣言? 它的范围是什么?

这与在源文件中共享variables有关,但是它是如何精确地工作的呢? 我在哪里使用extern

使用extern只有当你正在创build的程序包含多个源文件链接在一起时才有意义,其中一些定义的variables,例如源文件file1.c需要在其他源文件(如file2.c引用file2.c

理解定义variables和声明variables之间的区别是很重要的:

  • 当编译器被告知variables存在(这是它的types)时声明一个variables; 它不会为该variables分配存储空间。
  • 编译器为variables分配存储空间时定义一个variables。

你可以多次声明一个variables(尽pipe一次就足够了); 您只能在给定范围内定义一次。

声明和定义全局variables的最佳方法

虽然还有其他的方法,但声明和定义全局variables的干净可靠的方法是使用头文件file3.h来包含variables的extern 声明 。 标题包含在一个定义variables的源文件中,并由所有引用该variables的源文件包含。 对于每个程序,一个源文件(只有一个源文件)定义variables。 同样,一个头文件(也只有一个头文件)应该声明variables。

file3.h

 extern int global_variable; /* Declaration of the variable */ 

在file1.c

 #include "file3.h" /* Declaration made available here */ #include "prog1.h" /* Function declarations */ /* Variable defined here */ int global_variable = 37; /* Definition checked against declaration */ int increment(void) { return global_variable++; } 

file2.c中

 #include "file3.h" #include "prog1.h" #include <stdio.h> void use_it(void) { printf("Global variable: %d\n", global_variable++); } 

这是使用它们的最好方法。


接下来的两个文件完成了prog1的源代码:

prog1.h

 extern void use_it(void); extern int increment(void); 

prog1.c的

 #include "file3.h" #include "prog1.h" #include <stdio.h> int main(void) { use_it(); global_variable += 19; use_it(); printf("Increment: %d\n", increment()); return 0; } 
  • prog1使用prog1.cfile1.cfile2.cfile3.hprog1.h

方针

规则只能由专家打破,只有很好的理由:

  • 一个头文件只包含variables的extern声明 – 从来没有static或非限定的variables定义。
  • 对于任何给定的variables,只有一个头文件声明它(SPOT – 单点的真相)。
  • 源文件永远不会包含variables的extern声明 – 源文件始终包含声明它们的(唯一)标题。
  • 对于任何给定的variables,只有一个源文件定义了variables,最​​好是初始化它。 (虽然没有必要明确地初始化为零,但它没有任何危害,并且可以做一些很好的事情,因为程序中只能有一个特定全局variables的初始化定义)。
  • 定义variables的源文件还包含头部以确保定义和声明一致。
  • 一个函数应该永远不需要使用extern声明一个variables。
  • 尽可能避免全局variables – 使用函数。

如果你不是一个有经验的C程序员,你可以(也许应该)停止阅读。

不是很好的定义全局variables的方法

使用一些(实际上是很多)C编译器,你也可以避开所谓的variables的“通用”定义。 在这里,“常用”是指在Fortran中使用的一种技术,用于在源文件之间共享variables,使用(可能命名为)COMMON块。 这里发生的是,许多文件中的每一个都提供了variables的暂定义。 只要不超过一个文件提供了一个初始化的定义,那么各个文件最终将共享一个共同的variables定义:

file10.c

 #include "prog2.h" int i; /* Do not do this in portable code */ void inc(void) { i++; } 

file11.c

 #include "prog2.h" int i; /* Do not do this in portable code */ void dec(void) { i--; } 

file12.c

 #include "prog2.h" #include <stdio.h> int i = 9; /* Do not do this in portable code */ void put(void) { printf("i = %d\n", i); } 

这种技术不符合C标准的字母和“一个定义规则”,但是C标准把它作为一个定义规则的一个常见变体。 因为这种技术并不总是被支持的,所以最好避免使用它, 特别是如果你的代码需要可移植的话 。 使用这种技术,你也可以结束无意的types双关。 如果其中一个文件声明idouble而不是int ,那么C的types不安全的连接器可能不会发现这个不匹配。 如果你在64位intdouble的机器上,你甚至不会收到警告; 在32位int和64位double ,您可能会得到一个关于不同大小的警告 – 链接器将使用最大的大小,正如Fortran程序所占用的最大大小。

信息化附录J中的C标准中提到了这一点:

J.5.11多个外部定义

对象的标识符可以有多个外部定义,不论是否使用关键字extern; 如果定义不一致,或者多于一个被初始化,则行为是不确定的(6.9.2)。


接下来的两个文件完成了prog2的源prog2

prog2.h

 extern void dec(void); extern void put(void); extern void inc(void); 

prog2.c

 #include "prog2.h" #include <stdio.h> int main(void) { inc(); put(); dec(); put(); dec(); put(); } 
  • prog2使用prog2.cfile10.cfile11.cfile12.cprog2.h

警告

正如我在这里的评论中指出的那样,正如我对一个类似问题的回答所指出的那样,对一个全局variables使用多个定义会导致一个未定义的行为,这是标准的说“任何事情都可能发生”的方式。 其中一个可能发生的事情就是程序按照你的期望行事。 J.5.11大概说“你可能比你应得的运气更多”。 但是一个依赖于externvariables的多重定义的程序(无论有没有明确的“extern”关键字)都不是一个严格符合的程序,也不能保证在任何地方都能正常工作。 等同:它包含一个可能会或可能不会显示的错误。

违反准则

faulty_header.h

 int some_var; /* Do not do this in a header!!! */ 

注1:如果标题定义了没有extern关键字的variables,那么包含标题的每个文件都会创build一个variables的临时定义。

broken_header.h

 int some_var = 13; /* Only one source file in a program can use this */ 

注2:如果头文件定义并初始化variables,则给定程序中只有一个源文件可以使用头文件。

seldom_correct.h

 static int hidden_global = 3; /* Each source file gets its own copy */ 

注3:如果头文件定义了一个静态variables(有或没有初始化),那么每个源文件最后都有自己的“全局”variables的私有版本。

如果variables实际上是一个复杂的数组,例如,这可能会导致代码的极度重复。 它可以非常偶然地是一个明智的方法来达到某种效果,但这是非常不寻常的。


概要

使用我首先显示的标题技术。 它可靠,无处不在。 请注意,特别是声明global_variable的头文件包含在每个使用它的文件中,包括定义它的文件。 这确保了一切都是自洽的。

声明和定义function也有类似的问题 – 类似的规则适用。 但是问题是关于variables的,所以我只保留了variables的答案。

(完整的程序使用函数,所以函数声明已经悄悄进入了。我在头文件的函数声明前面使用关键字extern来匹配头文件中variables声明前面的extern ,很多人不希望在函数前面使用extern ,编译器不关心 – 只要你一致,最终也不会。)

原答复结束

如果你不是一个有经验的C程序员,你可能应该停止阅读这里。


晚大加法

避免代码复制

有时候(并且合法地)提出关于这里描述的“头文件中的声明,源文件中的定义”机制的一个问题是有两个文件需要保持同步 – 头文件和源文件。 这通常跟随着一个观察,即可以使用一个macros来使头部服务于双重任务 – 通常是声明variables,但是当包含头部之前设置了特定的macros时,它就定义了variables。

另一个问题可能是variables需要在许多“主程序”中的每一个中定义。 这通常是一个虚假的关注; 您可以简单地引入一个C源文件来定义variables,并将生成的对象文件与每个程序链接起来。

一个典型的scheme是这样工作的,使用file3.h说明的原始全局variables:

file3a.h

 #ifdef DEFINE_VARIABLES #define EXTERN /* nothing */ #else #define EXTERN extern #endif /* DEFINE_VARIABLES */ EXTERN int global_variable; 

file1a.c

 #define DEFINE_VARIABLES #include "file3a.h" /* Variable defined - but not initialized */ #include "prog3.h" int increment(void) { return global_variable++; } 

file2a.c

 #include "file3a.h" #include "prog3.h" #include <stdio.h> void use_it(void) { printf("Global variable: %d\n", global_variable++); } 

接下来的两个文件完成prog3的源prog3

prog3.h

 extern void use_it(void); extern int increment(void); 

prog3.c

 #include "file3a.h" #include "prog3.h" #include <stdio.h> int main(void) { use_it(); global_variable += 19; use_it(); printf("Increment: %d\n", increment()); return 0; } 
  • prog3使用prog3.cfile1a.cfile2a.cfile3a.hprog3.h

variables初始化

该scheme的问题如图所示,它不提供全局variables的初始化。 使用C99或C11以及macros的可变参数列表,您也可以定义一个macros来支持初始化。 (使用C89并且不支持macros中的可变参数列表,没有简单的方法来处理任意长的初始值设定项。)

file3b.h

 #ifdef DEFINE_VARIABLES #define EXTERN /* nothing */ #define INITIALIZER(...) = __VA_ARGS__ #else #define EXTERN extern #define INITIALIZER(...) /* nothing */ #endif /* DEFINE_VARIABLES */ EXTERN int global_variable INITIALIZER(37); EXTERN struct { int a; int b; } oddball_struct INITIALIZER({ 41, 43 }); 

反转#if#else块的内容,修复Denis Kniazhev识别的错误

file1b.c

 #define DEFINE_VARIABLES #include "file3b.h" /* Variables now defined and initialized */ #include "prog4.h" int increment(void) { return global_variable++; } int oddball_value(void) { return oddball_struct.a + oddball_struct.b; } 

file2b.c

 #include "file3b.h" #include "prog4.h" #include <stdio.h> void use_them(void) { printf("Global variable: %d\n", global_variable++); oddball_struct.a += global_variable; oddball_struct.b -= global_variable / 2; } 

显然,古怪的结构的代码并不是你通常所写的,但它说明了这一点。 INITIALIZER的第二个调用的第一个参数是{ 41 ,剩下的参数(在这个例子中是单数)是43 } 。 如果没有C99或对macros的可变参数列表的类似支持,则需要包含逗号的初始化程序是非常有问题的。

每个Denis Kniazhev包含正确的头文件file3b.h (而不是fileba.h


接下来的两个文件完成了prog4的源prog4

prog4.h

 extern int increment(void); extern int oddball_value(void); extern void use_them(void); 

prog4.c

 #include "file3b.h" #include "prog4.h" #include <stdio.h> int main(void) { use_them(); global_variable += 19; use_them(); printf("Increment: %d\n", increment()); printf("Oddball: %d\n", oddball_value()); return 0; } 
  • prog4使用prog4.cfile1b.cfile2b.cprog4.hfile3b.h

标题卫兵

任何头文件都应该被保护以防止重新引用,所以types定义(enum,struct或uniontypes或typedef通常)不会导致问题。 标准技术是将头部的主体包裹在头部警卫中,例如:

 #ifndef FILE3B_H_INCLUDED #define FILE3B_H_INCLUDED ...contents of header... #endif /* FILE3B_H_INCLUDED */ 

标题可能包含两次间接。 例如,如果file4b.h包含未显示的types定义的file1b.c ,而file1b.c需要同时使用头文件file4b.hfile3b.h ,则需要解决一些更棘手的问题。 显然,你可能会修改头文件列表来包含file4b.h 。 但是,您可能并不了解内部依赖关系,理想情况下,代码应该继续工作。

此外,它开始变得棘手,因为在包含file3b.h以生成定义之前,您可能会包含file4b.h ,但是file3b.h上的正常标头防护将防止重新包含头。

所以,最多需要一次包含file3b.h的主体用于声明,最多一次用于定义,但是您可能需要在一个单独的翻译单元中(TU – 源文件和它使用的标题的组合) 。

多variables定义包含

但是,这可以做一个不太不合理的约束。 我们来介绍一组新的文件名:

  • EXTERNmacros定义的external.h等。
  • file1c.h来定义types(特别是struct oddballoddball_struct的types)。
  • file2c.h来定义或声明全局variables。
  • file3c.c定义了全局variables。
  • file4c.c ,它只是使用全局variables。
  • file5c.c这表明你可以声明并定义全局variables。
  • file6c.c这表明你可以定义然后(试图)声明全局variables。

在这些例子中, file5c.cfile6c.c直接包含头文件file2c.h几次,但这是最简单的方法来显示机制的工作原理。 这意味着如果标题间接包含两次,这也是安全的。

这个工作的限制是:

  1. 定义或声明全局variables的头文件本身可能不定义任何types。
  2. 紧接在包含应定义variables的标头之前,您需要定义macrosDEFINE_VARIABLES。
  3. 定义或声明variables的头部具有程式化的内容。

external.h

 /* ** This header must not contain header guards (like <assert.h> must not). ** Each time it is invoked, it redefines the macros EXTERN, INITIALIZE ** based on whether macro DEFINE_VARIABLES is currently defined. */ #undef EXTERN #undef INITIALIZE #ifdef DEFINE_VARIABLES #define EXTERN /* nothing */ #define INITIALIZE(...) = __VA_ARGS__ #else #define EXTERN extern #define INITIALIZE(...) /* nothing */ #endif /* DEFINE_VARIABLES */ 

file1c.h

 #ifndef FILE1C_H_INCLUDED #define FILE1C_H_INCLUDED struct oddball { int a; int b; }; extern void use_them(void); extern int increment(void); extern int oddball_value(void); #endif /* FILE1C_H_INCLUDED */ 

file2c.h

 /* Standard prologue */ #if defined(DEFINE_VARIABLES) && !defined(FILE2C_H_DEFINITIONS) #undef FILE2C_H_INCLUDED #endif #ifndef FILE2C_H_INCLUDED #define FILE2C_H_INCLUDED #include "external.h" /* Support macros EXTERN, INITIALIZE */ #include "file1c.h" /* Type definition for struct oddball */ #if !defined(DEFINE_VARIABLES) || !defined(FILE2C_H_DEFINITIONS) /* Global variable declarations / definitions */ EXTERN int global_variable INITIALIZE(37); EXTERN struct oddball oddball_struct INITIALIZE({ 41, 43 }); #endif /* !DEFINE_VARIABLES || !FILE2C_H_DEFINITIONS */ /* Standard epilogue */ #ifdef DEFINE_VARIABLES #define FILE2C_H_DEFINITIONS #endif /* DEFINE_VARIABLES */ #endif /* FILE2C_H_INCLUDED */ 

file3c.c

 #define DEFINE_VARIABLES #include "file2c.h" /* Variables now defined and initialized */ int increment(void) { return global_variable++; } int oddball_value(void) { return oddball_struct.a + oddball_struct.b; } 

file4c.c

 #include "file2c.h" #include <stdio.h> void use_them(void) { printf("Global variable: %d\n", global_variable++); oddball_struct.a += global_variable; oddball_struct.b -= global_variable / 2; } 

file5c.c

 #include "file2c.h" /* Declare variables */ #define DEFINE_VARIABLES #include "file2c.h" /* Variables now defined and initialized */ int increment(void) { return global_variable++; } int oddball_value(void) { return oddball_struct.a + oddball_struct.b; } 

file6c.c

 #define DEFINE_VARIABLES #include "file2c.h" /* Variables now defined and initialized */ #include "file2c.h" /* Declare variables */ int increment(void) { return global_variable++; } int oddball_value(void) { return oddball_struct.a + oddball_struct.b; } 

下一个源文件完成了prog5prog6prog7的源代码(提供了一个主程序):

prog5.c

 #include "file2c.h" #include <stdio.h> int main(void) { use_them(); global_variable += 19; use_them(); printf("Increment: %d\n", increment()); printf("Oddball: %d\n", oddball_value()); return 0; } 
  • prog5使用prog5.cfile3c.cfile4c.cfile1c.hfile2c.hexternal.h
  • prog6使用prog5.cfile5c.cfile4c.cfile1c.hfile2c.hexternal.h
  • prog7使用prog5.cfile6c.cfile4c.cfile1c.hfile2c.hexternal.h

这个scheme避免了大多数问题。 如果定义variables的头文件(如file2c.h )包含在另一个定义variables的头文件(如file2c.h )中,则只会遇到问题。 除了“不这样做”以外,没有一个简单的方法。

您可以通过将file2c.h修改为file2d.h来部分解决该问题:

file2d.h

 /* Standard prologue */ #if defined(DEFINE_VARIABLES) && !defined(FILE2D_H_DEFINITIONS) #undef FILE2D_H_INCLUDED #endif #ifndef FILE2D_H_INCLUDED #define FILE2D_H_INCLUDED #include "external.h" /* Support macros EXTERN, INITIALIZE */ #include "file1c.h" /* Type definition for struct oddball */ #if !defined(DEFINE_VARIABLES) || !defined(FILE2D_H_DEFINITIONS) /* Global variable declarations / definitions */ EXTERN int global_variable INITIALIZE(37); EXTERN struct oddball oddball_struct INITIALIZE({ 41, 43 }); #endif /* !DEFINE_VARIABLES || !FILE2D_H_DEFINITIONS */ /* Standard epilogue */ #ifdef DEFINE_VARIABLES #define FILE2D_H_DEFINITIONS #undef DEFINE_VARIABLES #endif /* DEFINE_VARIABLES */ #endif /* FILE2D_H_INCLUDED */ 

问题变成'如果标题包含#undef DEFINE_VARIABLES ? 如果你从头文件中忽略了这个,并用#define#undef包装任何定义的调用:

 #define DEFINE_VARIABLES #include "file2c.h" #undef DEFINE_VARIABLES 

在源代码中(所以头文件不会改变DEFINE_VARIABLES的值),那么你应该干净。 必须记住写出额外的行是一个滋扰。 另一种可能是:

 #define HEADER_DEFINING_VARIABLES "file2c.h" #include "externdef.h" 

externdef.h

 /* ** This header must not contain header guards (like <assert.h> must not). ** Each time it is included, the macro HEADER_DEFINING_VARIABLES should ** be defined with the name (in quotes - or possibly angle brackets) of ** the header to be included that defines variables when the macro ** DEFINE_VARIABLES is defined. See also: external.h (which uses ** DEFINE_VARIABLES and defines macros EXTERN and INITIALIZE ** appropriately). ** ** #define HEADER_DEFINING_VARIABLES "file2c.h" ** #include "externdef.h" */ #if defined(HEADER_DEFINING_VARIABLES) #define DEFINE_VARIABLES #include HEADER_DEFINING_VARIABLES #undef DEFINE_VARIABLES #undef HEADER_DEFINING_VARIABLES #endif /* HEADER_DEFINING_VARIABLES */ 

这有点复杂,但似乎是安全的(使用file2d.h#undef DEFINE_VARIABLES中没有#undef DEFINE_VARIABLES file2d.h )。

file7c.c

 /* Declare variables */ #include "file2d.h" /* Define variables */ #define HEADER_DEFINING_VARIABLES "file2d.h" #include "externdef.h" /* Declare variables - again */ #include "file2d.h" /* Define variables - again */ #define HEADER_DEFINING_VARIABLES "file2d.h" #include "externdef.h" int increment(void) { return global_variable++; } int oddball_value(void) { return oddball_struct.a + oddball_struct.b; } 

file8c.h

 /* Standard prologue */ #if defined(DEFINE_VARIABLES) && !defined(FILE8C_H_DEFINITIONS) #undef FILE8C_H_INCLUDED #endif #ifndef FILE8C_H_INCLUDED #define FILE8C_H_INCLUDED #include "external.h" /* Support macros EXTERN, INITIALIZE */ #include "file2d.h" /* struct oddball */ #if !defined(DEFINE_VARIABLES) || !defined(FILE8C_H_DEFINITIONS) /* Global variable declarations / definitions */ EXTERN struct oddball another INITIALIZE({ 14, 34 }); #endif /* !DEFINE_VARIABLES || !FILE8C_H_DEFINITIONS */ /* Standard epilogue */ #ifdef DEFINE_VARIABLES #define FILE8C_H_DEFINITIONS #endif /* DEFINE_VARIABLES */ #endif /* FILE8C_H_INCLUDED */ 

file8c.c

 /* Define variables */ #define HEADER_DEFINING_VARIABLES "file2d.h" #include "externdef.h" /* Define variables */ #define HEADER_DEFINING_VARIABLES "file8c.h" #include "externdef.h" int increment(void) { return global_variable++; } int oddball_value(void) { return oddball_struct.a + oddball_struct.b; } 

接下来的两个文件完成了prog8prog9的源代码:

prog8.c

 #include "file2d.h" #include <stdio.h> int main(void) { use_them(); global_variable += 19; use_them(); printf("Increment: %d\n", increment()); printf("Oddball: %d\n", oddball_value()); return 0; } 

file9c.c

 #include "file2d.h" #include <stdio.h> void use_them(void) { printf("Global variable: %d\n", global_variable++); oddball_struct.a += global_variable; oddball_struct.b -= global_variable / 2; } 
  • prog8使用prog8.cfile7c.cfile9c.c
  • prog9使用prog8.cfile8c.cfile9c.c

但是,这些问题在实践中相对不太可能发生,特别是如果您采取标准的build议

避免全局variables


这个博览会是否错过了什么?

忏悔 :这里概述的“避免重复代码”计划是因为这个问题影响到我工作的一些代码(但不是自己的),并且是对答案第一部分概述的计划的一个琐碎的关注。 然而,原来的scheme留给你两个地方来修改,以保持variables的定义和声明同步,这是在外部variables声明分散在整个代码库(这真的很重要,当有成千上万的文件) 。 但是,名称为fileNc.[ch] (加上external.hexterndef.h )的文件中的代码表明可以使其工作。 显然,创build一个头文件生成器脚本并不难,因为它为您提供了定义和声明头文件的variables的标准化模板。

注意这些玩具程序只有足够的代码才能使它们变得有趣。 这些例子中的重复可以被删除,但不是为了简化教学的解释。 (例如: prog5.cprog8.c之间的区别是包含的头之一的名称,可以重新组织代码,这样main()函数就不会被重复,但是会隐藏更多比它显示。)

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

假设你有两个.c test1.c和test2.c 。 如果你定义一个全局variablesint test1_var;test1.c ,你想在test2.c访问这个variables,你必须使用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 

Extern是用来声明variables本身驻留在另一个翻译单元中的关键字。

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

如果你不把它声明为extern,你会得到2个variables,但是根本不相关,而且这个variables有多个定义的错误。

我喜欢把一个外部variables看作是你对编译器的承诺。

当遇到一个extern时,编译器只能找出它的types,而不能find它的“生命”,所以它不能parsing这个引用。

你说的是:“相信我,在链接的时候,这个参考是可以解决的。”

extern告诉编译器相信你这个variables的内存是在其他地方声明的,所以它不会尝试分配/检查内存。

因此,你可以编译一个引用到extern的文件,但是如果这个内存没有被声明的话,你就不能链接。

对于全局variables和库非常有用,但由于链接器没有inputcheck,所以很危险。

添加一个extern将variables定义变成一个variables声明 。 关于声明和定义之间的区别,请看这个线程 。

extern的正确解释是你告诉编译器。 你告诉编译器,尽pipe现在不存在,声明的variables将以某种方式被链接器find(通常在另一个对象(文件)中)。 无论你是否有外部声明,链接器将成为find所有东西并把它们放在一起的幸运人。

在C文件中的一个variables说example.c是给定的本地范围。 编译器期望variables在同一个文件example.c中有它的定义,当它找不到相同的时候,它会抛出一个错误。另一方面,函数默认具有全局作用域。 因此,你不必明确提到编译器“看起来老兄……你可能会在这里find这个函数的定义”。 对于包含声明文件的函数来说,就足够了(实际调用头文件的文件)。 例如,考虑以下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 step 2)./ ex

您将得到以下输出:a的值是<5>

extern关键字与variables一起用作其全局variables的标识。

它也表示你可以使用在任何文件中使用extern关键字声明的variables,尽pipe它是在其他文件中声明/定义的。

但是,这是如何精确地工作?

让我们看看GCC 4.8 ELF如何实现它

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更新ELF规范 “符号表”一章说明:

SHN_UNDEF此节表索引表示该符号未定义。 当链接编辑器将这个对象文件与另一个定义了指示符号的文件相结合时,这个文件对符号的引用将被链接到实际的定义。

这基本上是C标准给externvariables的行为。

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

First off, the extern keyword is not used for defining a variable; rather it is used for declaring a variable. I can say extern is a storage class, not a data type.

extern is used to let other C files or external components know this variable is already defined somewhere. Example: if you are building a library, no need to define global variable mandatorily somewhere in library itself. The library will be compiled directly, but while linking the file, it checks for the definition.

extern allows one module of your program to access a global variable or function declared in another module of your program. You usually have extern variables declared in header files.

If you don't want a program to access your variables or functions, you use static which tells the compiler that this variable or function cannot be used outside of this module.

extern is used so one first.c file can have full access to a global parameter in another second.c file.

The extern can be declared in the first.c file or in any of the header files first.c includes.

extern simply means a variable is defined elsewhere (ie, in another file).