Can't sort out multiple definition

574 views Asked by At

I have three header files ball.h, wrappers.h, graphics.h with corresponding .c-files. Each .c file includes its corresponding header file, all of which have include guards. In addition, wrappers.c includes graphics.h and wrappers.h includes ball.h, which defines a pair of const float (and more).

In a makefile I have for each of the above pairs an entry of the form name.o: name.c name.h with $(CC) -c $^. Finally, I have a test.c file (with a main function) which includes each of the above header files, and its makefile entry is test: test.c wrappers.o graphics.o ball.o with $(CC) $^ -o $@.

Compiling test leads to multiple definition error, saying that the aforementioned two const float are first defined in wrappers.o and ball.o.

I suppose this is because wrappers.h includes ball.h, but I have no idea how to resolve this, short of moving the offending variables or, worse, changing my code. Is the problem due to awkward includes, or because of the structure of the makefile?

ball.h excerpt:

#ifndef BALL_H
#define BALL_H

const float circleRadius = 0.025;
const float circleColor = 0;

typedef struct {
    float x,y; // etc
} ball_t;

// various function prototypes

#endif /* BALL_H */
1

There are 1 answers

0
Jonathan Leffler On BEST ANSWER

Converting comments into an answer.

In C, each time you include ball.h, you get global copies of the circleRadius and circleColor constants defined. Either they need to be static (as well as const float), or you need to declare them extern in the header (without an initializer) and define them fully in one source file.

This is an area where the rules in C++ are different from those in C; beware which compiler you use.

I haven't used extern before; how should I use it here? (And now that you mention it, static would probably be a good idea for this particular project, but I'd like to try the extern solution as well.)

In the header, write:

extern const float circleRadius;

In one source file, write:

const float circleRadius = 0.025;

Repeat for colour too. For integer values, you could consider using enum instead (See static const vs #define vs enum for more details.) For floating point (or, indeed, integer values), you could use this in the header instead:

#define CIRCLE_RADIUS 0.025

Obviously, you'd change the spelling of the references (all caps is customary for #define — and many enum — constants).

Also, as pointed out by WhozCraig in a comment, the question Why do we need the extern keyword in C if file scope declarations have external linkage by default? may be helpful.