#include 守卫不起作用 - 或者我不明白

Posted

技术标签:

【中文标题】#include 守卫不起作用 - 或者我不明白【英文标题】:#include guard doesn't work - or I don't understand it 【发布时间】:2016-03-17 14:45:25 【问题描述】:

我使用几个文件:main.c assembler.c fileHandlers.c handlers.c 此外,我还有几个头文件,包含常量、函数原型等。在其中一个(“datatypes.h”)中,我定义了一个字符串数组:

#ifndef DATATYPES_H
#define DATATYPES_H
const char *OPCODES = "str1",..., str15.
#endif

然后,我在所有文件中都包含了这个标题(因为他们都在某个时候使用它)。

这是我的生成文件:

main: main.o assembler.o filesHandler.o handlers.o
    gcc -g -Wall -ansi -pedantic main.o assembler.o filesHandler.o handlers.o -o main

main.o: main.c
    gcc -g -c -Wall -ansi -pedantic main.c -o main.o

assembler.o: assembler.c
    gcc -g -c -Wall -ansi -pedantic assembler.c -o assembler.o

filesHandler.o: filesHandler.c
    gcc -g -c -Wall -ansi -pedantic filesHandler.c -o filesHandler.o

handlers.o: handlers.c
    gcc -g -c -Wall -ansi -pedantic handlers.c -o handlers.o

当我尝试编译时,我收到以下错误:

gcc -g -c -Wall -ansi -pedantic assembler.c -o assembler.o
gcc -g -c -Wall -ansi -pedantic filesHandler.c -o filesHandler.o
filesHandler.c: In function ‘readFile’:
filesHandler.c:14:10: warning: unused variable ‘addressing’ [-Wunused-variable]
     char addressing[MAXWORD];
          ^
gcc -g -Wall -ansi -pedantic main.o assembler.o filesHandler.o handlers.o -o main
assembler.o:(.data+0x0): multiple definition of `OPCODES'
main.o:(.data+0x0): first defined here
filesHandler.o:(.data+0x0): multiple definition of `OPCODES'
main.o:(.data+0x0): first defined here
handlers.o:(.data+0x0): multiple definition of `OPCODES'
main.o:(.data+0x0): first defined here
collect2: error: ld returned 1 exit status
make: *** [main] Error 1

现在,我知道由于某种原因,数组在预处理后被多次定义,但我不知道为什么。 我阅读了关于#include 守卫的***文章,以及更多资源。他们都指示照我做的那样做,但它不起作用。

抱歉加载了这么多数据,但希望能避免不必要的跟进。

谢谢, 埃拉德

【问题讨论】:

第二行#include DATATYPES_H应该是#define DATATYPES_H。如果标志没有定义,我们还没有被包含,所以定义它。如果它被定义,我们已经被包含过一次,所以什么都不做。 抱歉,我打错了问题。在文件中它是#define,而不是#include,但它仍然不起作用。我解决了这个问题。 你可以让 OPCODES 本身为 const。 const char * const OPCODES = .... 【参考方案1】:

使用适当的包含保护(您似乎是,现在您已经更新了问题),一个头文件只包含一次每个翻译单元

一个翻译单元基本上是每个.cpp 文件,所有#includes 复制到其中的头文件。每个.cpp 文件都编译 成一个目标文件 (.o)。

然后将目标文件链接到最终的二进制文件中。

因此,每个标头中的代码在每个目标文件中都可以看到,然后在最终的二进制文件中出现多次。如果你只有 declarations 在头文件中,这很好,例如

const char* opcodes;

每个翻译单元都知道有一个名为opcodes 的字符串,但不会尝试创建实际变量。

但是如果你在头文件中有定义,这会被多次看到,这是不允许的,每个翻译单元都会尝试创建实际变量:

const char* opcodes = "foobar";

你应该把声明放在头文件中,并使用extern关键字,这样每个翻译单元都能看到名称,并使用它,但是将定义放在一个.cpp文件中,所以它实际上只创建了一次,其他人都引用同一个实例。

// foo.h included everywhere
#ifndef FOO_H
#define FOO_H
extern const char* opcodes;
#endif

.

// foo.cpp
#include "foo.h"
const char* opcodes = "foobar";

工作示例:

// a.h
#ifndef A_H
#define A_H
extern const char* foo;
#endif

.

// a.cpp
#include "a.h"
const char* foo = "Foo";

.

// main.cpp
#include <iostream>
#include "a.h"
int main() 
    std::cout << foo << '\n';

然后我分步编译和链接:

$ g++492 --std=c++14 -Wall -W -Werror -c a.cpp -o a.o
$ g++492 --std=c++14 -Wall -W -Werror -c main.cpp -o main.o
$ g++492 --std=c++14 main.o a.o -o main.tsk
$ ./main.tsk 
Foo

删除extern 我可以编译main.cpp,但不能编译a.cpperror: redefinition of 'const char* foo'

然后,如果我尝试将新的 main.o 与旧的 a.cpp 链接起来,我也会在那里出错,更像你原来的:multiple definition of 'foo'

【讨论】:

我按照你的建议做了——不过有一个注释——它不适用于“extern”,当我删除它时,一切正常——谢谢大家! 奇怪,如果没有 extern,我会期待像 error: redefinition of 'const char* opcodes' 这样的抱怨。我将在一个我现在正在测试的示例中进行编辑,我知道它肯定有效。 这是有道理的,但是为什么一个磅定义允许有多个定义呢?例如。 #ifndef FOO_H #define FOO_H #define FOO "Foo" #endif @cdamayor 我不太明白你的问题;您的示例显示 FOO_HFOO 分别定义一次。如果您的意思是这些符号最终会在链接的二进制文件中出现多次,那不是真的,预处理器只是进行源替换,名称 FOO_HFOO 在编译或链接时不再存在。也许您需要创建一个更详细和更完整示例的新问题。 其实这回答了我的问题,谢谢。我忘记了预处理器是如何工作的......【参考方案2】:

这是一个变量定义,它的默认链接是外部的。

这意味着您在包含标题的每个翻译单元中都有一个带有外部链接的定义。

将标题内容更改为仅包含声明

extern const char *OPCODES;

并将定义放在一个源文件中,而不是头文件中。

【讨论】:

【参考方案3】:

你应该在头文件中声明OPCODES,并在源文件中定义它。

数据类型.h

const char *OPCODES;

datatypes.c 或 datatypes.cpp

const char *OPCODES = "str1",..., str15;

否则会在所有包含“datatypes.h”的.o文件中生成“OPCODES”符号,然后链接错误。

【讨论】:

以上是关于#include 守卫不起作用 - 或者我不明白的主要内容,如果未能解决你的问题,请参考以下文章

我不明白为啥 CSS 转换在 keydown 上不起作用 [重复]

我不明白为啥这段代码不起作用 for int 错误 [重复]

承诺`终于'不起作用

组内的 Laravel 组不起作用,它没有看到命名空间参数

为啥“点子秀”或“点子列表”对我不起作用?

jQuery滚动顶部功能不起作用