#include guard不起作用 - 或者我不明白它

时间:2016-03-17 14:45:25

标签: c++ c header include-guards

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

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

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

这是我的makefile:

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警卫的维基百科文章,以及更多的资源。他们都像我一样指示做,但它不起作用。

很抱歉加载了这么多数据,但我希望它可以防止不必要的跟进。

谢谢, ELAD

3 个答案:

答案 0 :(得分:5)

使用正确的包含警卫(现在你已经更新了问题),只有一个在每个翻译单元中包含一个头文件。

翻译单元基本上是每个.cpp文件,其中#include复制到其中的所有头文件。每个.cpp文件编译分别编入目标文件.o)。

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

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

const char* opcodes;

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

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

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'

答案 1 :(得分:0)

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

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

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

extern const char *OPCODES;

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

答案 2 :(得分:0)

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

datatypes.h

const char *OPCODES;

datatypes.c或datatypes.cpp

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

或者将在所有.o文件中生成'OPCODES'的符号,包括'datatypes.h',然后链接错误。