我是否以错误的方式使用预处理器?

Am I Utilizing the Preprocessor the Wrong Way?

请注意:这不是家庭作业。该程序不完整且功能不全,但至少应该可以编译。

我正在使用 C Primer Plus Book 自学(简而言之,我是 C 的新手)。我几乎读完了整本书,并且一直在完成每一章的练习,有时我会跑题。这是那些时代之一。我 运行 遇到了一个特殊问题,我很确定它与预处理器指令有关。

我正在使用 MinGW(windows 的 gcc)并且它报告:

The error that gcc reports is:

nanfunct.c: multiple definition of 'keywords'
nanite.c: first defined here
etc... etc... more errors...

我很确定这是由于包含多个头文件引起的,但更重要的是我创建然后包含的头文件导致了这个问题。

这个问题似乎与指向字符数组(或基于字符串的数组)的指针有关,它们在编译时被复制,尽管我说只有在尚未预定义的情况下才定义它。

例如:

#ifndef MENU_OPTIONS
#   define MENU_OPTIONS ON
#   if MENU_OPTIONS == ON
        ...some code here...

        char * keywords[] = {
            "copy", "help", "line",
            "quit", "read", "write"
        };

        char * keyletters[] = {
            "c", "h", "l",
            "q", "r", "w"
        };
#   endif
#endif

I am using three files:

nanite.c -> source file for main()
nanfunct.c -> source file for functions
nanproto.h -> header file for nanite.c and nanfunct.c

nanite.cnanfunct.c 内部 #include nanproto.h

source files posted on pastebin:
nanproto.h -> Header File for nanite.c and nanfunct.c
nanite.c & nanfunct.c -> source files

为什么会这样?我以为 #ifndef 应该可以防止这样的事情发生?

您将 definitions 放入 .h 文件中是错误的。只有 声明 进入 .h 文件。

如果你把这个(定义):

char * keywords[] = { "foo" };

在一个.h文件中,然后将其包含在多个C文件中,无论您使用哪种#ifdefery,您最终都会得到该变量在项目的多个地方定义

要记住的关键是每个 .c 文件都是独立于其他文件编译的。这意味着,您是否在另一个 C 文件中 #defined 并不重要。

你的 .h 文件应该是这样的:

extern char *keywords[];

然后 一个 .c 文件应该提供定义。

您误解了预处理器的作用,或者 C 源文件的编译和链接方式。

每个源文件都是单独预处理的。因此,经过预处理后,nanfunct.c 包含 keywordskeyletters 的定义。然后将预处理后的源代码编译成目标文件 nanfunct.o.

预处理后,nanite.c还包含keywordskeyletters的定义。编译此预处理源以生成目标文件 nanite.o.

链接器然后尝试组合 nanfunct.o 和 nanite.o。它发现 keywordskeyletters 的定义不止一个,因此显示错误消息并中止。

如果您希望某些东西在多个源文件中可用,通常的模式是将声明放在头文件中,将定义放在一个源文件中。

移动这个:

char * keywords[] = {
        "copy", "help", "line",
        "quit", "read", "write"
};

char * keyletters[] = {
        "c", "h", "l",
        "q", "r", "w"
};

变成 nanite.c nanfunct.c (不是两者)。将这些 声明 添加到 nanite.h:

extern char * keywords[];
extern char * keyletters[];

这样,定义只包含在一个目标文件中。

请注意,这仅适用于全局变量和函数。它不适用于结构、联合、枚举或 typedef,因为它们不包含在目标文件中。