我该如何使用extern在源文件之间共享变量?
我该如何使用extern在源文件之间共享变量?
我知道C语言中的全局变量有时会带有extern
关键字。什么是extern
变量?它的声明是什么样的?它的作用域是什么?
这与在源文件之间共享变量有关,但具体是如何工作的呢?我在哪里使用extern
?
extern
变量是一个变量的声明(感谢sbi提供的修正),它在另一个翻译单元中定义。这意味着变量的存储在另一个文件中分配。
假设您有两个 .c
文件 test1.c
和 test2.c
。如果您在 test1.c
中定义全局变量 int test1_var;
并且您想要在 test2.c
中访问此变量,则必须在 test2.c
中使用 extern int test1_var;
。
完整示例:
$ cat test1.c int test1_var = 5; $ cat test2.c #includeextern 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
只有当你构建的程序由多个源文件链接在一起时,在其中一些源文件(例如file1.c
)定义的变量需要在其他源文件(如file2.c
)中引用时,extern
才有意义。
-
当编译器知道一个变量的存在(和它的类型)时,变量被声明;此时并没有分配变量的存储空间。
-
当编译器分配变量的存储空间时,变量被定义。
你可以多次声明一个变量(虽然一次就足够了),但是在给定范围内只能定义它一次。变量定义也是一个声明,但不是所有变量声明都是定义。
声明和定义全局变量的最佳方法
声明和定义全局变量的清晰、可靠的方法是使用头文件来包含变量的extern
声明。
头文件被定义变量的一个源文件和引用变量的所有源文件所包含。对于每个程序,一个源文件(仅一个源文件)定义变量。同样,一个头文件(仅一个头文件)应该声明变量。
头文件至关重要;它可以在独立的转换单元(Think源文件)之间进行交叉检查,确保一致性。
虽然有其他方法可以实现,但这种方法简单且可靠。
它由file3.h
,file1.c
和file2.c
演示:
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" #includevoid use_it(void) { printf("Global variable: %d\n", global_variable++); }
这是声明和定义全局变量的最佳方法。
下面两个文件完整地展示了prog1
的源代码:
展示的所有完整程序都使用了函数,因此函数声明已经出现了。
C99和C11要求在使用函数之前声明或定义函数(而C90不需要,这是合理的)。
我在头文件中的函数声明前使用关键字extern
以保持一致性,以匹配头文件中变量声明前的extern
。
许多人不喜欢在函数声明前使用extern
;编译器并不在意——最终,只要你始终如一,至少在一个源文件中,我也不在意。
prog1.h
extern void use_it(void); extern int increment(void);
prog1.c
#include "file3.h" #include "prog1.h" #includeint main(void) { use_it(); global_variable += 19; use_it(); printf("Increment: %d\n", increment()); return 0; }
prog1
使用prog1.c
,file1.c
,file2.c
,file3.h
和prog1.h
。
文件prog1.mk
是仅用于prog1
的makefile。
它适用于自公元纪元之后的大多数版本的make
。
它不特定于GNU Make。
prog1.mk
# Minimal makefile for prog1 PROGRAM = prog1 FILES.c = prog1.c file1.c file2.c FILES.h = prog1.h file3.h FILES.o = ${FILES.c:.c=.o} CC = gcc SFLAGS = -std=c11 GFLAGS = -g OFLAGS = -O3 WFLAG1 = -Wall WFLAG2 = -Wextra WFLAG3 = -Werror WFLAG4 = -Wstrict-prototypes WFLAG5 = -Wmissing-prototypes WFLAGS = ${WFLAG1} ${WFLAG2} ${WFLAG3} ${WFLAG4} ${WFLAG5} UFLAGS = # Set on command line only CFLAGS = ${SFLAGS} ${GFLAGS} ${OFLAGS} ${WFLAGS} ${UFLAGS} LDFLAGS = LDLIBS = all: ${PROGRAM} ${PROGRAM}: ${FILES.o} ${CC} -o $@ ${CFLAGS} ${FILES.o} ${LDFLAGS} ${LDLIBS} prog1.o: ${FILES.h} file1.o: ${FILES.h} file2.o: ${FILES.h} # If it exists, prog1.dSYM is a directory on macOS DEBRIS = a.out core *~ *.dSYM RM_FR = rm -fr clean: ${RM_FR} ${FILES.o} ${PROGRAM} ${DEBRIS}
指南
仅由专家违反的规则,并且只有有充分理由时才违反:
-
头文件仅包含变量的
extern
声明——不包括static
或未经限定的变量定义。
针对任何给定的变量,只有一个头文件声明它(SPOT——单一真相点)。
源文件从不包含变量的extern
声明——源文件始终包含(唯一)声明它们的头文件。
对于任何给定的变量,仅有一个源文件定义该变量,最好还初始化它。(虽然没有必要显式地初始化为零,但它不会有害且有些好处,因为程序中只能有一个特定全局变量的已初始化定义)。
定义变量的源文件还应包含该头文件,以确保定义和声明一致。
一个函数永远不应使用extern
声明变量。
尽可能避免全局变量——使用函数代替。
此答案的源代码和文本可在我的SOQ(Stack Overflow Questions)GitHub存储库的src/so-0143-3204子目录中获取。
如果您不是有经验的C程序员,可以(也许应该)在此停止阅读。
定义全局变量的不太好的方法
在某些(确实是许多)C编译器中,您可以采用所谓的“通用”方式定义变量。“通用”在这里是指Fortran中用于在源文件之间共享变量的技术,使用(可能命名的)COMMON块。这里发生的是,多个文件提供变量的试探定义。只要不超过一个文件提供一个已初始化定义,那么各个文件最终会共享变量的共同单一定义:
file10.c
#include "prog2.h" long l; /* Do not do this in portable code */ void inc(void) { l++; }
file11.c
#include "prog2.h" long l; /* Do not do this in portable code */ void dec(void) { l--; }
file12.c
#include "prog2.h" #includelong l = 9; /* Do not do this in portable code */ void put(void) { printf("l = %ld\n", l); }
这种技巧不符合C标准和“单一定义规则”的字面意思,它是官方未定义的行为:
使用具有外部链接的标识符,但程序中不存在该标识符的正好一个外部定义,或者标识符未被使用并且存在标识符的多个外部定义(6.9)。
外部定义是一个同时也是函数(除了内联定义)或对象的定义的外部声明。如果使用具有外部链接的标识符作为表达式的一部分(不作为
sizeof
或_Alignof
操作符的操作数,其结果是一个整数常量),则在整个程序中正好存在一个标识符的外部定义;否则,标识符不得超过一个。161)
161)因此,如果具有外部链接的标识符未在表达式中使用,则不需要为其提供外部定义。
然而,C标准还将其列入信息性附录J中,作为常见扩展之一。
一个标识符的对象可能有一个以上的外部定义,有或没有显式使用关键词extern;如果这些定义不一致或有一个以上被初始化,那么行为是未定义的(6.9.2)。
因为这种技巧并不总是被支持,最好避免使用它,尤其是如果你的代码需要可移植性。使用这种技巧,你可能也会遇到无意中的类型转换。
如果上述文件中有一个将l
声明为double
而不是long
的话,C语言的类型不安全的链接器可能不会发现不匹配。如果你在一台64位的long
和double
的机器上,甚至不会得到警告;在一台32位的long
和64位的double
的机器上,你可能会收到不同大小的警告——链接器会使用最大的大小,而Fortran程序会选择任何公共块中的最大大小。
请注意,GNU 10.1.0版本的GCC,于2020年5月7日发布,更改了默认的编译选项,使用-fno-common
,这意味着默认情况下,上述代码不再链接,除非你用-fcommon
覆盖默认选项(或使用属性等——请查看链接)。
接下来两个文件完成了prog2
的源代码:
prog2.h
extern void dec(void); extern void put(void); extern void inc(void);
prog2.c
#include "prog2.h" #includeint main(void) { inc(); put(); dec(); put(); dec(); put(); }
prog2
使用了prog2.c
、file10.c
、file11.c
、file12.c
和prog2.h
。
警告
正如在此处的注释中所述,以及我对类似问题的回答中所述,对全局变量使用多个定义会导致未定义的行为(J.2;§6.9),这是标准的方式表明“任何事情都有可能发生”。
其中可发生的一件事是程序表现符合您的预期;J.5.11大致表示:“您可能比您应得的更幸运”。
但是,一个依赖于多个外部变量定义的程序-无论是否带有显式的“extern”关键字-都不是严格遵守程序,并且不能在任何地方保证正常工作。
同样地:它包含一个可能会显示或不会显示的错误。
违反指南
当然,有许多方式可以违反这些指南。
偶尔,可能有很好的理由来违反指南,但是这种情况极为罕见。
faulty_header.h
int some_var; /* Do not do this in a header!!! */
注意1:如果头文件没有定义extern
关键字的变量,则包含头文件的每个文件都会创建变量的尝试性定义。
正如前面所述,这通常可以正常工作,但C标准并不保证它会工作。
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 */
注意三:如果头文件定义了静态变量(带有或不带有初始化),那么每个源文件都会得到其自己私有的"全局"变量。
例如,如果变量实际上是一个复杂的数组,这可能会导致代码的极度重复。这在很少情况下是实现某种效果的明智方式,但这非常不寻常。
总结
使用我首先展示的头文件技术。
它在各个地方都能可靠地工作。
特别要注意的是,声明global_variable
的头文件包含在使用它的每个文件中,包括定义它的文件。
这确保了一切的自洽性。
声明和定义函数时会出现类似的问题,应用同样的规则。
但这个问题是关于变量的,所以我只回答有关变量的问题。
原始回答结束
如果你不是经验丰富的C程序员,你可能应该在这里停止阅读。
重要补充
避免代码重复
有时人们会对这里描述的"头文件声明变量,源文件定义变量"机制提出一个(合理的)关注:需要保持两个文件同步--头文件和源文件。然后通常会跟随一个观察,即可以使用宏使头文件具有双重作用--通常声明变量,但在头文件被包含之前设置特定宏时,它定义变量。
另一个关注可能是变量需要在多个"主程序"中定义。这通常是一个虚假的顾虑;只需引入一个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" #includevoid use_it(void) { printf("Global variable: %d\n", global_variable++); }
接下来的两个文件完成了prog3
的源代码:
prog3.h
extern void use_it(void); extern int increment(void);
prog3.c
#include "file3a.h" #include "prog3.h" #includeint main(void) { use_it(); global_variable += 19; use_it(); printf("Increment: %d\n", increment()); return 0; }
prog3
使用了prog3.c
、file1a.c
、file2a.c
、file3a.h
、prog3.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
块的内容,修复了由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" #includevoid 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或类似的支持宏变长参数列表,包含逗号的初始化表达式就非常棘手。
根据 Denis Kniazhev 的建议,正确的头文件 file3b.h
被引入,而不是 fileba.h
。
下面两个文件完整地提供了 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" #includeint main(void) { use_them(); global_variable += 19; use_them(); printf("Increment: %d\n", increment()); printf("Oddball: %d\n", oddball_value()); return 0; }
prog4
使用了prog4.c
、file1b.c
、file2b.c
、prog4.h
、file3b.h
。
头文件保护
任何头文件都应该受到保护以避免重复包含,以免类型定义(如枚举、结构体或联合类型,或者通常的 typedef)造成问题。标准技术是将头文件体包含在头文件保护中,例如:
#ifndef FILE3B_H_INCLUDED #define FILE3B_H_INCLUDED ...contents of header... #endif /* FILE3B_H_INCLUDED */
头文件可能会间接地被包含两次。例如,如果 file4b.h
包含了一个未显示的类型定义而包括了 file3b.h
,而 file1b.c
需要同时使用头文件 file4b.h
和 file3b.h
,那么你需要解决一些更棘手的问题。显然,你可以修改头文件列表,只包含 file4b.h
。然而,你可能不了解内部依赖关系——优秀的代码应当一直可用。
此外,如果你先包含 file4b.h
来生成定义,然后再包含 file3b.h
,那么通常的头文件保护会阻止重新包含头文件,这就变得棘手了。
因此,你需要最多一次包含 file3b.h
的体来进行声明,最多一次包含 file3b.h
的体来进行定义,但你可能需要在一个单独的翻译单元(TU——一个源文件和它使用的头文件的组合)中都需要。
带有变量定义的多重包含
然而,可以在不太苛刻的限制下实现。我们引入一组新的文件名:
-
external.h
用于定义 EXTERN 宏,等等。 -
file1c.h
用于定义类型(特别是struct oddball
,即oddball_struct
的类型)。 -
file2c.h
用于定义或声明全局变量。 -
file3c.c
用于定义全局变量。 -
file4c.c
简单地使用全局变量。 -
file5c.c
展示了您可以先声明再定义全局变量。 -
file6c.c
展示了您可以先定义然后(尝试)声明全局变量。
在这些示例中,file5c.c
和 file6c.c
直接多次包含头文件
file2c.h
,但这是展示机制最简单的方式。这意味着如果头文件间接包含两次,它也是安全的。
这种实现的限制是:
-
定义或声明全局变量的头文件本身不能定义任何类型。
-
在包含应该定义变量的头文件之前,定义宏 DEFINE_VARIABLES。
-
定义或声明变量的头文件具有样式化内容。
external.h
/* ** This header must not contain header guards (likemust 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" #includevoid 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; }
下一个源文件为 prog5
, prog6
和 prog7
提供了主程序:
prog5.c
#include "file2c.h" #includeint main(void) { use_them(); global_variable += 19; use_them(); printf("Increment: %d\n", increment()); printf("Oddball: %d\n", oddball_value()); return 0; }
-
prog5
使用prog5.c
,file3c.c
,file4c.c
,file1c.h
,file2c.h
,external.h
。 -
prog6
使用prog5.c
,file5c.c
,file4c.c
,file1c.h
,file2c.h
,external.h
。 -
prog7
使用prog5.c
,file6c.c
,file4c.c
,file1c.h
,file2c.h
,external.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 */
问题变成了“是否应该在头文件中包含 #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 (likemust 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
,在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
和prog9
的源代码:
prog8.c
#include "file2d.h" #includeint 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" #includevoid use_them(void) { printf("Global variable: %d\n", global_variable++); oddball_struct.a += global_variable; oddball_struct.b -= global_variable / 2; }
-
prog8
使用prog8.c
,file7c.c
,file9c.c
。 -
prog9
使用prog8.c
,file8c.c
,file9c.c
。
然而,在实践中,这些问题相对不太可能发生,特别是如果您遵循标准建议避免使用全局变量。
避免使用全局变量
这篇文章漏掉了什么内容吗?
认罪:此处概述了“避免重复代码”的方案,是因为该问题影响到我所工作的某些代码(但不是我拥有的代码),而且是第一个答案中概述的方案的一个琐碎的关注点。但是,原始方案只留下了两个地方需要修改,以使变量定义和声明同步,这比将外部变量声明散布在整个代码库中要前进一大步(当总文件数达到数千个时,这确实很重要)。但是,具有`fileNc.[ch]`名称的文件中的代码(以及`external.h`和`externdef.h`)表明可以使其正常工作。显然,可以很容易地创建一个头文件生成器脚本,以为变量定义和声明头文件提供标准模板。
注:这些是玩具程序,只有足够的代码使它们稍微有些趣味。实例中存在重复,可以消除,但为了简化教学解释而未消除。例如:`prog5.c`和`prog8.c`之间的差异在于所包含的头文件名称。可以重新组织代码,以使`main()`函数不重复,但它会掩盖更多内容。