为什么非extern可以在C / C ++中的.h文件中?

以此文件为例,有许多非外部结构,如:

struct list_head source_list; 

当多个编译单元包含此头文件时,它如何工作?

应该有错误报告,相同的符号被定义两次,对吗?

技术上应该存在,但是这种使用已存在多年并且无法根除(已经尝试过;经常会有一些供应商决定将其作为错误,并在大约一百个左右的错误报告之后恢复)。 小心翼翼地, .h文件应该声明它是extern并且一个 .c / .cpp文件应该定义它。

简而言之,当您没有指定顶级变量的链接( staticextern等)时,它被声明为“常见”。 在链接时,如果对该变量的所有引用都是相同的大小(和类型,如果可用),那么它将被分配一次,并且所有引用都指向它。 如果链接器为同一个变量找到不同的大小/类型/链接,则会引发错误。

编辑:这显然让人感到困惑。 这里:

 jinx:1714 Z$ cat foo.h int foo; extern void bar(); jinx:1715 Z$ cat foo.c #include "foo.h" int main(int argc, char **argv) { bar(); return 0; } jinx:1716 Z$ cat bar.c #include "foo.h" void bar(void) { return; } jinx:1717 Z$ gcc -Wall foo.c bar.c -o foo jinx:1718 Z$ ./foo jinx:1719 Z$ _ 

注意完全没有关于int foo被多重定义的错误。 就是我一直想说的。

这个术语是“暂定”:

具有文件范围而没有初始化程序且没有存储类说明符或存储类说明符为静态的对象的标识符声明构成暂定定义。 如果翻译单元包含一个或多个标识符的暂定定义,并且翻译单元不包含该标识符的外部定义,那么行为就像翻译单元包含该标识符的文件范围声明一样,复合类型为翻译单元的结尾,初始化程序等于0。

所以这在C中有很好的定义(但经常不赞成)。

这个struct list_head source_list; 字段在其他结构中声明,因此它们不是符号。

其他(顶级)结构的声明具有不同的名称,所以它也可以。

编辑

请注意,此标头的所有变量都标有extern

确实应该有一个extern 。 但是,该变量没有明确的定义 ,因此编译器会将其标记为extern。

如果有的话,你会得到一个链接器错误

 struct list_head source_list = { 0 }; 

…因为这确实为每个翻译单元定义了一次符号(因此链接器会抱怨)。