global-variables - C 如何使用extern来共享源文件之间的变量?

  显示原文与译文双语对照的内容

我知道 extern 中的全局变量有时有关键字。 什么是 extern 变量? 什么是声明? 它的范围是什么?

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

时间:

使用 extern 仅在你正在构建的程序由多个源文件组成时才是相关的,其中一些定义的变量( 如源文件 file1.c ) 需要在其他源文件中引用,如 file2.c

重要的是要在定义 理解一个变量和一个变量声明: 区别

  • 当编译器为变量分配存储时,变量为定义的
  • 在那个point,一个变量被声明 当编译器中会被告知这个变量存在( 这就是它的类型) ;他不允许分配的存储的变量

你可以多次声明一个变量( 尽管已经足够了) ;你只能在给定范围内定义一次。

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

虽然还有其他方法,但是声明和定义全局变量的可靠方法是使用头文件 file3.h 来包含变量的extern 声明 。 标题由定义变量的一个源文件和引用该变量的所有源文件包含。 对于每个程序,一个源文件( 只有一个源文件) 定义了变量。 类似地,一个头文件( 只有一个头文件) 应该声明变量。

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: %dn", global_variable++);
}

这是最好的方法。


于相关完成source:接下来的两个文件

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: %dn", increment());
 return 0;
}

  • prog1 使用 prog1.cfile1.cfile2.cfile3.hprog1.h

指南

规则只被专家打破,并且只有很好的理由:

  • 头文件只包含变量的extern 声明—从不 static 或者非限定变量定义。
  • 对于任何给定的变量,只有一个头文件声明它( 点—单点真理) 。
  • 源文件从不包含变量的extern 声明—源文件总是包含声明它们的( 唯一) 头。
  • 对于任何给定的变量,恰好一个源文件定义了变量,最好是初始化它。 ( 尽管没有必要显式初始化为零,但它没有任何害处,因为在程序中只能初始化一个特定全局变量的定义) 。
  • 定义变量的源文件也包含标题以确保定义和声明一致。
  • 函数不应该使用 extern 声明变量。
  • 尽可能避免全局变量—使用函数。

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

不是定义全局变量的好方法

有了一些( 确实,很多) C 编译器,你就可以摆脱变量的'公用'定义。 ''公共'这里,是指在Fortran中使用一个( 可能命名为) 公共块 block 。 这里的情况是,许多文件都提供了变量的暂定定义。 只要没有多个文件提供一个初始化定义,那么各种文件最终会共享一个变量的公共单一定义:

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 = %dn", i); }

这里技术不符合C 标准和'一个定义规则'的字母,但标准将它的列为一个定义规则上的常见变体。 因为这种技术并不总是支持的,最好避免使用它,尤其是当你的代码需要便携式 。 使用这种技巧,你也可以用无意识的类型技巧。 如果将一个声明为 i的文件作为 double 而不是 int,则type-unsafe连接器的可能不会发现不匹配。 如果你在一台带有 64位 intdouble的机器上,你甚至没有收到警告;在机器的32位 int 和 64位 double 中,你可能会收到一个警告,链接器将使用的大小与程序的最大大小完全相同。

这是在附录J 中的标准中提到的一个通用扩展:

J.5.11 多个外部定义

对象标识符的外部定义可能不止一个,或者没有关键字extern的显式使用;如果定义不一致,或者不止一个被初始化,行为是未定义的( 6.9.2 ) 。


于相关完成source:接下来的两个文件

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

警告

就像前面评论把我的答案在这里,并按照规定在一个类似 问题,使用多个定义为全局变量导致未定义的行为,这就是标准的方式,导致说"任何事情都可能发生"。 可能发生的事情之一是程序按照预期的方式运行;J.5.11 说,大约是"你可能比你想要的更多"。 但是,依赖于外部变量—的多个定义( 带有或者不带显式'外部'关键字 — )的程序不是严格一致的程序,并且不能保证在任何地方都能工作。 等价:它包含一个 Bug,它可能也可能不显示自身。

违反准则

faulty_header.h


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

注意 1: 如果标题定义了不带 extern 关键字的变量,那么每个包含该标题的文件都会创建变量的暂定定义。

broken_header.h


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

注意 2: 如果标题定义并初始化了变量,那么一个给定程序中只有一个源文件可以使用该头文件。

seldom_correct.h


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

注意 3: 如果报头定义了一个静态变量( 带或者不初始化),那么每个源文件都会使用它自己的'全局'变量的私有版本。

如果变量实际上是一个复杂数组,这可能导致代码的极度重复。 它偶尔可以是一种合理的方式来达到某种效果,但这相当不寻常。


摘要

使用我首先展示的头部技术。 它可以可靠地工作。 特别注意,声明 global_variable的头包含在每个使用它的文件中,包括定义它的文件。 这确保了一切都是 self-consistent 。

声明和定义函数时出现类似的问题—类似规则应用。 但是问题是关于变量的,所以我只把变量的答案保留在变量上。

( 完整的程序使用函数,所以函数声明已经在。 我在头部的函数声明前面使用关键字 extern 来匹配标题中变量声明前面的extern 。 很多人都不喜欢在函数前面使用 extern ;编译器不关心 —,并且最终也不像你一样。

原始答案结束

如果你不是一个经验丰富的程序员,你应该在这里停止阅读。


晚期 Addition

避免代码重复

有时候,( 和合法的) 提出的一个关于'标题中的声明,源中的定义'机制的问题是,有两个文件需要保持同步—头和源代码。 这通常是接着列举了一个观测,一个宏可以取得较大,故之前的标头通常用处,可以—声明该变量,但是当一个特定的宏被设置则包括在内,它定义了变量而不是标头。

另一个问题是变量需要在多个'主程序'中定义。 这通常是一个虚假的问题;你可以简单地引入一个C 源文件来定义变量,并链接每个程序生成的对象文件。

典型的方案是这样的,使用 file3.h 中的原始全局变量:

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: %dn", global_variable++);
}


于相关完成source:接下来的两个文件

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: %dn", increment());
 return 0;
}

  • prog3 使用 prog3.cfile1a.cfile2a.cfile3a.hprog3.h

变量初始化

这里方案的问题在于它不提供全局变量的初始化。 对于宏的C99或者C11和变量参数列表,你可以定义一个宏来支持初始化。 ( 使用C89和不支持宏中变量参数列表,没有简单的方法来处理任意长的初始值设定项。)

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 块的反向内容,通过 Denise Kniazhev 确定 Bug

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: %dn", global_variable++);
 oddball_struct.a += global_variable;
 oddball_struct.b -= global_variable/2;
}

显然,古怪结构的代码并不是你通常编写的代码,但是它说明了。 第二个调用 INITIALIZER的第一个参数是 { 41,其余参数( 这里示例中的单数) 是 43 } 。 没有对宏的变量参数列表的C99或者类似的支持,需要包含逗号的初始值设定项是非常有问题的。

正确的标题 file3b.h 包含( 而不是 fileba.h ) 每 Denis Kniazhev


于相关完成source:接下来的两个文件

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: %dn", increment());
 printf("Oddball: %dn", oddball_value());
 return 0;
}

  • prog4 使用 prog4.cfile1b.cfile2b.cprog4.hfile3b.h

头部卫士

任何标头都应该受reinclusion保护,因此类型定义( 枚举,结构或者联合类型或者typedef一般) 不会导致问题。 标准技术是将头部的主体包装在头部保护中,例如:


#ifndef FILE3B_H_INCLUDED
#define FILE3B_H_INCLUDED

...contents of header...

#endif/* FILE3B_H_INCLUDED */

标题可能被间接包含两次。 例如如果 file4b.h 为没有显示的类型定义包含 file3b.h,并且 file1b.c 需要使用头 file4b.hfile3b.h,那么你就有一些棘手的问题需要解决。 显然,你可以修改标题列表,只包括 file4b.h 。 但是,你可能没有意识到内部依赖关系—和代码应该继续工作。

下文是进一步 file3b.h 可以防止这里标头,它开始变得非常棘手,因为你可能包含如 file3b.h 来生成定义。但正常之前 file4b.h 头 guards.

在一个翻译单元这样的主体,你需要分别包括 file3b.h 为声明一次,最多只能获得定义。不过你也许需要至多一次,都行,

包含变量定义的多个包含

然而,它可以受到不太合理的约束。 让我们介绍一个新的文件名集:

  • 外部宏定义等的external.h
  • file1c.h 定义类型( struct oddballoddball_struct的类型) 。
  • 定义或者声明全局变量的file2c.h
  • file3c.c 定义全局变量。
  • file4c.c,它只使用全局变量。
  • file5c.c 表示你可以声明并定义全局变量。
  • file6c.c,表明你可以定义,然后( 尝试) 声明全局变量。

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

这项工作的限制是:

  1. 定义或者声明全局变量的标头本身不能定义任何类型。
  2. 在包含定义变量的标题之前,请定义宏 DEFINE_VARIABLES 。
  3. 定义或者声明变量的标头具有样式化的内容。

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: %dn", 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; }


prog5prog6 和相关源下一个源文件中完成了

prog5.c


#include"file2c.h"
#include <stdio.h>

int main(void)
{
 use_them();
 global_variable += 19;
 use_them();
 printf("Increment: %dn", increment());
 printf("Oddball: %dn", 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

这个方案避免了大多数问题。 如果定义变量( 比如 file2c.h )的标头由另一个定义变量的标头( 说 file7c.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 */

#define 和这个问题变为从页眉和包装任何定义 invocation:'标头是否包含 #undef DEFINE_VARIABLES'如果省略,


#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 ) 。

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; }


prog8 相关和完成source:接下来的两个文件

prog8.c


#include"file2d.h"
#include <stdio.h>

int main(void)
{
 use_them();
 global_variable += 19;
 use_them();
 printf("Increment: %dn", increment());
 printf("Oddball: %dn", oddball_value());
 return 0;
}

file9c.c


#include"file2d.h"
#include <stdio.h>

void use_them(void)
{
 printf("Global variable: %dn", global_variable++);
 oddball_struct.a += global_variable;
 oddball_struct.b -= global_variable/2;
}

  • prog8 使用 prog8.cfile7c.cfile9c.c
  • prog9 使用 prog8.cfile8c.cfile9c.c

然而,这些问题在实践中是不可能发生的,尤其是当你采取标准的建议

避免全局变量


这个博览会是否会错过?

在第一部分的answer, Confession: ( 但不要自己) 上,我完成了,开发了'避免重复代码'方案这里概述的,因为这个问题影响着一些代码,并且是一个琐碎的需要关注的是方案 outlined. 然而,原始方案离开你,只不过要修改两个地方,以保持变量的定义和声明同步,会有很大的进步比拥有exernal变量声明分散在代码基础( 当总共有数千个文件时,这真的很重要) 。 但是,名为 fileNc.[ch] ( 加上 external.hexterndef.h )的文件中的代码表明它可以工作。 显然,创建一个头生成器脚本来为定义和声明头文件的变量提供标准化模板是很困难的。

铌这些是玩具程序受到只是刚好足够的代码以让他们略微有趣。 示例中有重复的重复,但不能简化教学解释。 ( 例如:prog5.cprog8.c 之间的区别是包含的标题的名称) 。 可以重新组织代码,这样 main() 函数就不会被重复,但它会隐藏超过它所揭示的。

extern 变量是在另一个翻译单元中定义的变量的声明( 感谢sbi的修正) 。 这意味着变量在另一个文件中定义。。

假设你有两个 .c -files test1.ctest2.c 。 如果定义一个全局变量在 test1.cint test1_var;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 = %dn", test1_var);
 return 0;
}
$ gcc test1.c test2.c -o test
$./test
test1_var = 5

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

因此,你可以决定在翻译单元中使用一个变量,然后从另一个变量中访问它,然后将它声明为 extern,然后由链接器解析。

如果你没有将它声明为外部变量,你将得到 2个同名的变量,但它们根本不相关,并且有多个变量定义的错误。

extern告诉编译器要信任你该内存,该变量被声明在其它地方,所以虽然没有尝试分配/检查内存。

因此,你可以编写一个文件,该文件具有引用在外部问题得到解决,但是你无法链接如果那个内存未声明的某个角落

用于全局变量和库,但危险,因为链接器不类型检查。

我想把一个外部变量看作是你对编译器的承诺。

当遇到外部时,编译器只能找到它的类型,而不是它的"使用寿命",所以它不能解析引用。

你说的是"相信我"。 在链接时,这里引用将被解析。"

添加一个 extern 的变量定义成一个变量声明拐弯。 关于声明和定义之间的区别,请参见这个线程

外部的正确解释是你告诉编译器。 你告诉编译器,尽管现在没有出现,但是声明的变量会被链接器( 通常在另一个对象中( 文件) 找到。 如果你有一些外部声明或者不做任何事情,链接器将是幸运的人来找到一切并把它放在一起。

extern关键字与变量一起使用,用作全局变量。

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

在文件中,文件中的变量表示 example.c 是局部作用域。 编译器期望变量在同一个文件 example.c 中有它的定义,当它没有找到相同的时候,它将在另一个方面抛出一个 error.A 函数。 因此,你不必显式地提交给编译器"看伙计。。你可以在这里找到这个函数的定义"。 对于包含包含它的声明的文件的函数。( 你实际调用头文件的文件) 。 例如考虑以下 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 example.c example1.c 步骤 2 ) 。/

获得以下输出: <的值为5>

extern允许程序的一个模块访问在程序的另一个模块中声明的全局变量或者函数。 你通常在头文件中声明了外部变量。 如果你不想让程序访问你的变量或者函数,你可以使用static告诉编译器这个变量或者函数不能在这个模块之外使用。

...