I'm in the process of moving code written to be compiled for one chip onto another chip.
One issue that's come up is a multitude of multiple definition errors. Some of which appear to be due to the linker for the first chip letting me be lazy with declaring variables extern when they are to be used across multiple source files. I didn't use extern at all previously (declare and define a variable in something.h, use it in something.cpp and other source files that included something.h) and it compiled and linked perfectly well.
I've fixed these issues well enough, I believe: now my variables that are shared have this pattern:
Something.h
extern int foo;
Something.cpp
int foo = 0;
//using foo to do stuff
Main.cpp
#include "Something.h"
//using foo to do stuff
All good.
Here's the bit I don't understand, and can't seem to find any answers to here or on Google. I've noticed the same multiple definition errors being caused by variables that are declared and defined in Something.h and used only in Something.cpp.
Something.h has an include guard, so I don't think it's due to Something.h being included multiple times somewhere in my program.
The error goes away if I declare it as extern and define it in the cpp file, but this feels wrong to me. I believe the extern is not needed to link a variable between a Something.h and Something.cpp.
Any advice would be greatly appreciated, I'd really like to understand what I'm missing here.
(I'm compiling for ESP32 using the Arduino IDE by the way.)
If you declare your variable in the header file:
#ifndef GLOBAL_H
#define GLOBAL_H
int foo = 0;
#endif
In every include of your header file or translation unit, a new instance of your integer is created. As you mentioned, to avoid this, you need to declare the item as "extern" in the header file and initialize it in the implementation file:
// .h
extern int foo;
// .cpp
int foo = 0
A more C++ way to do that can be something like this:
#ifndef GLOBAL_H
#define GLOBAL_H
struct Global {
static int foo;
};
#endif
And in your cpp file:
#include "variables.h"
int Global::foo = 0;
C++17 fixes this problem with inline variables, so you can do:
#ifndef GLOBAL_H
#define GLOBAL_H
inline int foo = 0;
#endif
See How do inline variables work? for more information.