1

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 */
Erik Vesterlund
  • 481
  • 6
  • 19
  • 3
    *"...includes ball.h, which defines a pair of const float (and more)."* - this question should include that code. Sans-code, we resort to soothsaying. My crystal ball tells me you're outright defining those variables in the header, rather than declaring them `extern` in the header, and actually defining them in a single C-translation unit. Alternatively, you could live with multiple definitions by way of declaring+defining them `static` in the header. For constants, either method should work. – WhozCraig Aug 27 '17 at 21:02
  • 1
    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 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. – Jonathan Leffler Aug 27 '17 at 21:08
  • Thanks guys. 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.) – Erik Vesterlund Aug 27 '17 at 21:10
  • 1
    `extern const float circleRadius;` in the header. In one source file: `const float circleRadius = 0.025;`. Repeat for colour too. For integer values, you could consider using `enum` instead. For floating point, you could use `#define CIRCLE_RADIUS 0.025` in the header instead. – Jonathan Leffler Aug 27 '17 at 21:18
  • Worked like a charm, thanks! Formulate it as an answer if you want and I'll give you the "points" :) – Erik Vesterlund Aug 27 '17 at 21:20
  • 1
    [This question](https://stackoverflow.com/questions/13300239/why-do-we-need-the-extern-keyword-in-c-if-file-scope-declarations-have-externa) may prove useful. – WhozCraig Aug 27 '17 at 21:20

1 Answers1

4

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.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278