Am I Utilizing the Preprocessor the Wrong Way?

80 Views Asked by At

Please Note: This is NOT homework. The program is not complete and does not fully function, but should, at the very least, compile.

I am in the midst of a self-taught process using the C Primer Plus Book (In short I'm new to C). I've nearly completed reading the whole book and have been working through the exercises for each chapter and from time to time I have gone off on a tangent. This is one of those times. I've run in to a peculiar issue and I'm pretty sure is related to the preprocessor directives.

I am using MinGW (gcc for windows) and it reports:

The error that gcc reports is:

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

I'm pretty sure that this is caused by the inclusion of multiple header files, but more importantly the header file that I created and then included is causing this issue.

The issue seems to be linked to pointer-to-char-arrays (or string based arrays) which are duplicated at compile time even though i say to define it only if has not been predefined already.

for example:

#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

Inside of nanite.c and nanfunct.c I #include nanproto.h

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

Why is this happening? I thought that #ifndef was supposed to keep things like this from happening?

2

There are 2 best solutions below

0
On BEST ANSWER

You are misunderstanding what the preprocessor does, or how C source files are compiled and linked.

Each source file is preprocessed separately. So, after preprocessing, nanfunct.c contains definitions of keywords and keyletters. The preprocessed source is then compiled into the object file nanfunct.o.

After preprocessing, nanite.c also contains definitions of keywords and keyletters. This preprocessed source is compiled to produce the object file nanite.o.

The linker then tries to combine nanfunct.o and nanite.o. It finds that there is more than one definition of keywords and keyletters, so it displays an error message and aborts.

If you want something to be available in multiple source files, the usual pattern is to put the declaration in a header file, and the definition in one source file.

Move this:

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

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

into either nanite.c or nanfunct.c (not both). Add these declarations to nanite.h:

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

That way, the definitions are only included in one object file.

Note that this only applies to global variables and functions. It does not apply to structs, unions, enums, or typedefs, as these aren't included in object files.

2
On

You're making a mistake by putting definitions in your .h files. Only declarations go in .h files.

If you put this (a definition):

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

in a .h file, and then include it in multiple C files, it doesn't matter what kind of #ifdefery you use, you will still end up with that variable being defined in multiple places in your project.

The key thing to remember is that each .c file is compiled independently of the others. That means, it doesn't matter if you #defined something in another C file or not.

Your .h file should have something like this:

extern char *keywords[];

And then exactly one .c file should provide the definition.