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 thecircleRadiusandcircleColorconstants defined. Either they need to bestatic(as well asconst float), or you need to declare themexternin 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
enuminstead (Seestatic constvs#definevsenumfor 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
externkeyword in C if file scope declarations have external linkage by default? may be helpful.