【问题标题】:Why can non-extern be in .h files in C/C++?为什么非外部可以在 C/C++ 中的 .h 文件中?
【发布时间】:2011-03-23 11:58:04
【问题描述】:

this file为例,有很多非外部结构如:

struct list_head source_list;

当这个头文件被多个编译单元包含时如何工作?

同一个符号被定义两次应该会报错吧?

【问题讨论】:

  • “编译单元”通常称为“翻译单元”。

标签: c symbols header-files


【解决方案1】:

技术上应该有,但是这种用法已经存在多年并且不可能根除(已经尝试过;每隔一段时间就会有一些供应商决定将其设为错误,并在前一百个左右的错误报告后恢复)。学究式地,.h 文件应该声明它 extern 并且 one .c/.cpp 文件应该定义它。

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

编辑:这显然令人困惑。这里:

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 被多重定义的错误。 是我一直想说的。

【讨论】:

  • 你是说module.h中的struct list_head source_list;是一个变量的定义?
  • @ssmir:这是一个声明,而不是定义(除非它已初始化,如 elsepage 所述)。它被隐式标记为“common”,因此链接器将(只要所有声明相互兼容)自己定义它。
  • 好的,那么它是一个全局变量的声明吗?
  • @ssmir:有效。我试图区分全局变量的正确声明发生一次,但由于历史原因,您可以多次声明它,只要它们都兼容,链接器就会使其工作。它相当混乱和令人困惑(我记得 GNU binutils 的人们抱怨需要支持它的工作),这就是为什么经常有人试图杀死它。
  • 谢谢,编译器接受这样的多重声明这一事实非常有趣。然而,module.h 中的 struct list_head source_list; 不是全局变量声明,因为它们发生在其他结构内(即 module_usemodule)。
【解决方案2】:

这个术语是“暂定定义”:

一个标识符的声明 具有文件范围但没有 初始化器,并且没有 存储类说明符或与 存储类说明符静态, 构成一个 暂定定义。如果翻译单元包含一个或多个 暂定定义 标识符,并且翻译单元不包含外部 该标识符的定义,然后 行为就像翻译单元包含一个 文件范围声明 标识符,复合类型截至 翻译单元,带有初始化器 等于 0。

因此,这在 C 中已得到很好的定义(但通常不受欢迎)。

【讨论】:

  • @compile-fan 您问题中的声明没有声明任何类型的变量,它只是声明了结构的一个字段。此外,声明它的结构不会产生符号。这也不是一个暂定的定义。
  • @compile-fan,是的,它可以。看,上面写着:one or more tentative definitions for an identifier.
  • @ssmir,您是对的,问题中指出的示例与您在回答中给出的一样(+1 来自我:)。但正如问题本身所呈现的那样,它是一个暂定定义。
  • 谢谢 :) 看来他的意思是暂时的定义。
【解决方案3】:

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

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

编辑

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

【讨论】:

    【解决方案4】:

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

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

    struct list_head source_list = { 0 };
    

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

    【讨论】:

    • 这是一个定义,所谓暂定。
    猜你喜欢
    • 1970-01-01
    • 2017-11-11
    • 2011-09-05
    • 1970-01-01
    • 1970-01-01
    • 2021-10-28
    • 2015-03-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多