【发布时间】:2018-10-27 23:04:03
【问题描述】:
我有一个相对较小的项目,只有几个文件(.c 和 .h)。我一直在与多个包含错误作斗争(我认为)。因此,我创建了一个 master.h 头文件,其中包含所需的每个其他头文件。所有的头文件都有
#ifndef _MY_HEADER
#define _MY_HEADER
… Header body
#endif
防止多重包含的警卫。我的每个文件都包含顶部的 master.h 文件。这就是我期望它的工作方式。
- 编译的第一个文件会看到 #include "master.h"
- 由于这是第一次处理此文件 __MASTER 尚未定义,因此它将处理该文件。
- 编译器会包含下一个头文件,同样,它还没有被处理,所以编译器会处理它,然后它会被定义,并且不会再次被处理。
- 对于 master.h 中的所有头文件,这将继续进行,直到所有文件都被处理并定义为不再被处理。
- 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
externto 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