为什么定义 "extern_" 而不是使用 "extern"?

Why define "extern_" rather than using "extern"?

我试图制作一个简单的词法分析器,我发现了这个 github 页面:https://github.com/DoctorWkt/acwj/tree/master/01_Scanner 在他的源代码中我看到了:

data.h:

...
#ifndef extern_
 #define extern_ extern
#endif

extern_ int Line;
extern_ int Putback;
extern_ FILE *Infile;
...

main.c:

...
#define extern_
#include "data.h"
#undef extern_
...

如果我只使用不起作用的 extern 关键字,但它与 extern_ 一起使用,那么有什么区别?

#define extern_ 告诉预处理器在看到 extern_ 时什么都不替换。

所以在这种情况下 extern_ 没有任何意义。

但我敢打赌他们不会使用其他文件 #define extern_。在那种情况下,头文件中的 #define extern_ extern 被激活,因为 #ifndef extern_ 为真(extern_ 尚未定义)。这告诉预处理器用 extern 替换 extern_。因此,在一个文件中,变量的定义没有 extern,而在所有其他文件中,它们都有 extern。 (为什么有用?如果你知道 extern 是如何工作的,你就会知道为什么)

extern_宏的直接作用是控制extern关键字是否出现在变量声明中。原则上,它也可以用来替代其他一些关键字或添加限定词,但这似乎是偶然的。

在这一点上,重要的是要注意问题中提供的代码并不代表所引用的 GitHub 项目中的代码。在 GitHub 上,变量声明出现在 header 中,而不是 main.c 中。这与为什么这样的设施有用直接相关。

特别是,考虑在整个项目中执行的两个备选方案之间的区别:

  • 项目#include中的大部分C源文件header没有定义宏extern_。在那些情况下,header 本身定义宏 extern_ 以扩展到关键字 extern,导致这些翻译单元中的这些声明:

     extern int Line;
     extern int Putback;
     extern FILE *Infile;
    
  • 文件 main.c 比较特殊。它定义宏 extern_ 以扩展为空,并且在该定义的范围内包含 header。 header 然后依赖于提供的宏定义,因此 在单独的翻译单元中 ,结果声明是

      int Line;
      int Putback;
      FILE *Infile;
    

前者与后者的区别在于前者是纯粹的声明,而后者是初步定义。这很重要,因为程序访问的每个 object 和外部 linkage 必须在一个翻译单元中定义。包含给定 object 暂定定义的翻译单元肯定包含 object 的定义(这比听起来要复杂一些)。

那么,总的来说,效果就是同一个header可以用在两个不同的角色上:一方面,默认情况下,声明外部object的标识符,以便它们可以从其他翻译单元访问,另一方面可以在一个选定的翻译单元中定义它们,以便它们实际存在于程序中。

If I use just the extern keyword that dosn't work but it work with extern_ so what is the difference?

声明变量 extern 而不提供初始值设定项构成对变量在程序某处定义的承诺,但它本身 导致 变量被定义.如果给定的变量没有以任何其他方式在程序的任何地方声明,那么所有这些承诺都没有实现,并且结果行为是未定义的。通常,这将以 link 失败的形式出现。

只要我提到初始值设定项,似乎谨慎的做法是注意将初始值设定项写入 header 中的声明并不是一个可行的解决方案,因为每个包含 header 的翻译单元都会具有所有变量的定义,而在整个程序中每个变量的定义不得超过一个。该行为将再次未定义。在实践中,该程序被接受的可能性更大,但它仍然是错误的。

最后,我注意到 整个使用 extern_ 宏的业务有点像 hack,不应被视为传统业务。做到这一点的规范方法是 header 简单地声明所有变量 extern,并且在选定的 C 中每个变量都有一个 单独的 定义源文件(不一定都在同一个文件中)。示例:


data.h

extern int Line;
extern int Putback;
extern FILE *Infile;

main.c

#include "data.h"

int Line /* optionally with an initializer here */;
int Putback  /* optionally with an initializer here */;
FILE *Infile  /* optionally with an initializer here */;

// ...

other.c

#include "data.h"

// no (additional) declarations or definitions of the variables declared in data.h