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 */
Converting comments into an answer.
In C, each time you include
ball.h
, you get global copies of thecircleRadius
andcircleColor
constants defined. Either they need to bestatic
(as well asconst float
), or you need to declare themextern
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.
In the header, write:
In one source file, write:
Repeat for colour too. For integer values, you could consider using
enum
instead (Seestatic const
vs#define
vsenum
for more details.) For floating point (or, indeed, integer values), you could use this in the header instead:Obviously, you'd change the spelling of the references (all caps is customary for
#define
— and manyenum
— 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.