【问题标题】:C/C++ - Conditional header file inclusion not workingC/C++ - 条件头文件包含不起作用
【发布时间】:2011-10-23 21:27:59
【问题描述】:

我的项目中有三个文件。

交流
b.c
测试.h

test.h 声明一个

namespace test_namespace {
    int i;
    void f1();
};

test.h 也被

包围
#ifndef __x
#define __x
...
#endif

现在, a.c 包括 test.h 并且 b.c 也包括 test.h 。 a.c有main()函数,b.c有test_namespace::f1()的实现

然而,在编译这个时,我得到一个链接错误 -

"test_namespace::i is already defined in <b.c's object file mapping in /tmp>"

如果我已经注意在 test.h 中包含条件编译预处理器指令,为什么它同时包含在文件 a.c 和 b.c 中?

另外值得注意的是,如果我将 b.c 单独编译为共享库,然后在链接 a.c 的目标文件时将其用作共享库,则不会出现此错误。

有人可以向我解释上述错误,特别是面对条件编译指令吗?

【问题讨论】:

  • 您的条件头文件包含工作正常,但您的代码已损坏。全局变量只能定义一次。
  • 条件编译指令(通常称为包含守卫)并不像您认为的那样做。它们防止头文件被两次包含在 same 源文件中,它们不会防止头文件被两次包含在 不同 源文件中。如果这就是他们的工作方式,那就太神奇了,我们不相信。
  • “事物”/以 2 个下划线或一个下划线和一个大写字母开头的标识符保留用于实现。请修改您的标头保护命名。像其他人一样把它改成HEADERNAME_H,也许以PROJECTNAME_为前缀。

标签: c++ c header linker conditional


【解决方案1】:

条件包含用于防止同一源文件而不是整个项目的标头被包含两次。假设您有标题a.hb.hb.h #includes a.h。然后,如果 c.c 需要两个标题中的内容,则 #include 两者都需要。由于 C 预处理器使用文字文本替换,当它包含 b.h 时,您的文件中现在将有两个 #include "a.h" 指令,对多个声明造成严重破坏。 (编辑:澄清为什么在这种情况下会遇到问题。)

【讨论】:

  • 我很高兴得到纠正,但我相信预处理器首先运行,并且不知道除了#includes 之外的“项目”是什么。它一次处理一个文件,并且没有全局逻辑。
【解决方案2】:

包含保护用于保护编译单元构建中的多个 header 包含。当您有两个单独的代码文件和一个标头(例如您的示例)时,它们不是必需的。

(所以当test.c 使用a.hb.h 时要多考虑,在那些b.h 需要#include a.h 的情况下。)

但这是关于包含保护约定的作用以及在这种情况下它如何不为您购买任何东西的说明。您遇到的特定技术问题(正如其他人指出的那样)是您基本上在两个不同的目标文件中定义了相同的变量,并且当链接器将所有内容放在一起时,它不知道您是否想要来自 @ 的变量987654327@或b.o

( 注意:虽然编译器通常可以设置为覆盖事物并使用 namespace 之类的功能构建 C++ 代码,即使扩展名是 .c - 您可能应该使用其他东西,例如 .cppC++ code file extension? .cc vs .cpp)

【讨论】:

    【解决方案3】:

    标头保护 (#ifndef .. #define .. #endif) 正在正常工作。 a.cb.c 都包含 test.h,因此它们都会获得该标头的副本。 (当你编译一个程序时,#include 所做的就是将头文件的内容复制粘贴到源文件中。)

    因为他们都有一个标题的副本,他们都定义了变量test_namespace::i。当链接器尝试将 a.c 生成的代码与 b.c 生成的代码链接时,它发现它们都定义了该变量。它不知道该做什么,所以它没有完成并输出错误。

    【讨论】:

      【解决方案4】:

      当你说的时候发生了什么

      int i;
      

      这实际上做了两件事

      1) 声明符号i

      2) 在目标文件中为i 保留一些空间和一个符号

      诀窍是 (1) 每个文件只应执行一次(实际上在这种情况下您可以重复它)——这就是为什么你有条件包含的原因,你已经正确地完成了——但是 (2) 应该每个程序

      只能执行一次

      解决方案是让标题做

      // Only declare -- don't create a symbol
      extern int i;
      

      在 *.c 文件中做

      int i;
      

      【讨论】:

        【解决方案5】:

        您在标题中定义test_namespace::i。你可能是什么 want 是标题中的extern int i;,以及其中一个中的定义 源文件。

        【讨论】:

          【解决方案6】:

          您不能在头文件中声明变量。 符号 test_namespace::i 由 a.c 和 b.c 导出。链接器找到两者,但不知道使用哪一个。

          你想在 test.h 中做的是:

          namespace test_namespace {
              extern int i;
              void f1();
          }
          

          然后在 eather a.c 或 b.c:

          中声明 test_namespace::i
          namespace test_namespace {
              int i;
          }
          

          【讨论】:

          • 其实你可以declare它们,但是你不能define它们(除非它们是静态的......)。这里的问题是int i; 被解析为定义。 (您的代码仍然正确)
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2015-04-23
          • 1970-01-01
          • 1970-01-01
          • 2020-11-03
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多