【问题标题】:Multiple inclusion error for header file(s) - C [duplicate]头文件的多重包含错误 - C [重复]
【发布时间】:2018-10-27 23:04:03
【问题描述】:

我有一个相对较小的项目,只有几个文件(.c 和 .h)。我一直在与多个包含错误作斗争(我认为)。因此,我创建了一个 master.h 头文件,其中包含所需的每个其他头文件。所有的头文件都有

#ifndef _MY_HEADER
#define _MY_HEADER
… Header body
#endif

防止多重包含的警卫。我的每个文件都包含顶部的 master.h 文件。这就是我期望它的工作方式。

  1. 编译的第一个文件会看到 #include "master.h"
  2. 由于这是第一次处理此文件 __MASTER 尚未定义,因此它将处理该文件。
  3. 编译器会包含下一个头文件,同样,它还没有被处理,所以编译器会处理它,然后它会被定义,并且不会再次被处理。
  4. 对于 master.h 中的所有头文件,这将继续进行,直到所有文件都被处理并定义为不再被处理。
  5. master.h 文件也是如此。一旦处理完毕,由于有守卫,不会再次处理。

这不应该防止多重包含吗?

所以这里是错误。

Building target: My_Project_Bootloader.axf
Invoking: GNU ARM C Linker
arm-none-eabi-gcc -g3 -gdwarf-2 -mcpu=cortex-m3 -mthumb -T "C:/Users/Greg/SimplicityStudio/v4_workspace/My_Project_Bootloader/GNU ARM v4.9.3 - Debug/My_Project_Bootloader_custom.ld" -nostdlib -L"C:\GCC_STUFF" --specs=nosys.specs -Xlinker --gc-sections -Xlinker -Map="My_Project_Bootloader.map" -lm -lgcc -lc -o My_Project_Bootloader.axf "./Source/aeabi_memset-thumb.o" "./Source/crt0.o" "./Source/em_emu.o" "./Source/functions.o" "./Source/main.o" "./Source/startup_efm32jg1b.o" "./Source/interrupts.o" 
./Source/main.o:(.rodata.const_ModBusIDReg+0x0): multiple definition of `const_ModBusIDReg'
./Source/functions.o:(.rodata.const_ModBusIDReg+0x0): first defined here
collect2.exe: error: ld returned 1 exit status
make: *** [My_Project_Bootloader.axf] Error 1

我已经编辑/删除了这篇文章中的一些信息,因为事实证明它是不相关的。似乎链接器遇到的“多重定义”问题不是由于实际的“多重定义”或任何变量的包含。相反,它似乎是由于同时声明和定义变量(在本例中为闪存中的 const)所致。一旦我将声明拆分为头文件,并将实际定义或分配拆分为 .c 文件,问题就消失了。以下是解决问题“变量”之一的示例。我现在将更加关注变量的声明和定义方面。使用的工具是 GCC。感谢所有的cmets。

// boot.h
// declare const array
const unsigned char const_ModBusIDReg[7][48];


// boot.c
#include boot.h

// define const array
const unsigned char const_ModBusIDReg[7][48] =
{
"String1",          // reg00
"String2",          // reg01
"String3",          // reg02
"String4",          // reg03
"String5",          // reg04
"String6",          // reg05
"String7"           // reg06
 };

【问题讨论】:

  • 请注意,通常不应创建以下划线开头的函数或变量名称。 C11 §7.1.3 Reserved identifiers (部分)说: — 所有以下划线开头的标识符以及大写字母或另一个下划线始终保留供任何使用。所有以下划线开头的标识符在普通名称空间和标记名称空间中始终保留用作具有文件范围的标识符。 另请参阅What does double underscore (__const) mean in C?
  • 我已经从每个多重包含保护名称中删除了前导下划线,并且错误结果是相同的。我希望链接器脚本文件中的“.gnu.linkonce.sectionname”可以防止在链接时多次包含,但我想我不明白这是做什么的。
  • 标题保护的拼写是一个小问题,与主要问题相切,即您可能在标题中定义而不是声明全局变量。确保标题declare 而不是define 变量。另见How do I use extern to share variables between source files?
  • 虽然我有很多定义,但它们用于常量而不是变量。这是 XmitBuf 和 RcvBuf 问题的示例。这是为了将这些缓冲区强制为所需大小的固定部分,并且它们只在一个头文件的一个位置声明。 __attribute__((section(".xmitbuf")))unsigned char XmitBuf[256]; __attribute__( ( section(".rcvbuf") ) )unsigned char RcvBuf[256];
  • 嗯,包含这些定义的标头只能包含在一个源文件中。但是,您引用的名称不是链接器抱怨的名称。您可能对错误消息中的名称有类似的定义。这些需要类似于extern __attribute__( ( section(".xmitbuf")))unsigned char XmitBuf[256]; 等,在标题中以extern 开头,然后一个(也是唯一一个)源文件包含没有extern 的声明。你所拥有的东西不起作用;你将不得不改变它。没有源代码很难提供更多帮助(参见 MCVE — minimal reproducible example)。

标签: c header multiple-inclusions


【解决方案1】:

问题是链接器问题,而不是每个源文件问题。阻止标题多次包含在单个翻译单元(TU - 源文件加上包含的文件)中不会解决此问题。

您似乎有四个符号(const_ModBusIDRegconst_ModBusRegAttrconst_ModBusRegDefaults, const_ModBusRegLimits) 在多个源文件中定义,可能是因为您在标头中定义而不是声明它们。

标头中引用的变量必须以 extern 为前缀 — 否则,它们是定义,而不是声明。

【讨论】:

  • 它应该可以工作。根据 C 标准的6.2.2 Linkages of identifiers, paragraph 5:“如果对象标识符的声明具有文件范围且没有存储类说明符,则其链接是外部的。”因此,为文件范围变量保留extern 无效。 Paragraph 2 声明:“在构成整个程序的翻译单元和库集合中,具有外部链接的特定标识符的每个声明都表示相同的对象或函数。”
  • 是的,它的链接是外部的。它也是一个tentative definition,但如果在 TU 末尾没有替代(例如初始化)定义,它就会成为实际定义。然后有J.5.11 说你经常会侥幸逃脱——但很明显,正在使用的编译器不会让 OP 侥幸逃脱。这可能是因为它使用了严格的 ODR(一个定义规则),也可能是因为标头使用初始化器定义了变量。
【解决方案2】:

通常#ifndef HEADER_NAME 就像您避免任何包括冲突一样。但是您说“我的每个文件都包含 master.h”,所以我建议您的 .c 不应包含除他们拥有的关联 .h 之外的任何其他内容。 (可能已经是这样了)。

另外,请注意不要包含.c

我不知道你的链接器脚本,但你用什么工具来编译?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-23
    • 2020-07-05
    • 2013-04-10
    • 2016-03-30
    相关资源
    最近更新 更多