I want do define a variable in the header and be able to use it in multiple files.
e.g. have variable a
defined somewhere and be able to use/change it in both p1.cpp and p2.cpp
Here is an example with 3 simple files of what I'm trying to do.
// vars.hpp
#ifndef VARS_HPP
#define VARS_HPP
int a = 1;
float b = 2.2;
void double_vars();
#endif
// vars.cpp
#include "vars.hpp"
void double_vars () {
a *= 2;
b *= 2;
}
// vars_main.cpp
#include <cstdio>
#include "vars.hpp"
int main () {
printf("a: %d; b: %f\n", a, b);
double_vars();
printf("a: %d; b: %f\n", a, b);
}
now, compiling the above with:
g++ -Wall -W -g vars.cpp vars_main.cpp -o vars && ./vars
gives me the following errors:
/tmp/ccTPXrSe.o:(.data+0x0): multiple definition of `a'
/tmp/ccnc1vof.o:(.data+0x0): first defined here
/tmp/ccTPXrSe.o:(.data+0x4): multiple definition of `b'
/tmp/ccnc1vof.o:(.data+0x4): first defined here
can someone explain to me why this happens? I have guards in the header file so as far as I understand it should only be included once so there should not be multiple declarations
In the header file, write this:
extern int a;
extern float b;
This will declare the variables.
In one of the CPP files, write this:
int a = 1;
float b = 2.2;
This will define the variables. It does not matter in which CPP file you put the definitions, as long as the variables are defined exactly once.
First off, don't do this. Global mutable state is usually a bad thing.
The problem is that once the linker finishes combining the files in your include statments, it has seen this twice, once in main.cpp and once in vars_main.cpp:
int a = 1;
float b = 2.2;
If you only put this in the header file, it shows up in multiple translation units, once for each cpp
file that includes your header. It's impossible for the compiler to know which a and b you're talking about.
The way to fix this is to declare them extern
in the header:
extern int a;
extern float b;
This tells the compiler that a and b are defined somewhere, not necessarily in this part of the code.
Then, in one of your cpp files you would define them:
int a = 1;
float b = 2.2;
That way, there's one well-defined place for the storage for a
and b
to live, and the compiler can connect the dots properly.
Collin already described the problem and the solution, but I want to emphasize just how bad a style this is.
As you've found out even from this simple exercise, using globals in this manner is often a Bad Thing. Aside from the compiler issues you've already found, sharing state between functions using globals causes the following problems:
it makes the function itself harder to reuse in other programs, because the function is expecting the presence of those symbols;
changes to one function may cause problems in other functions that share that state (i.e., the changes are not localized to the function);
code becomes harder to debug, since anyone can modify a
and b
at any time.
Ideally, functions should communicate exclusively through parameters, return values, and exceptions (where applicable); there are times where sharing state is the right answer, but those are relatively few and far between.
Since you're obviously using a C++ compiler, I'm going to do this the C++ way, using references instead of pointers. I'm also going to use C++ iostream
routines instead of cstdio
(write either C or C++; trying to mix the two only leads to heartburn).
vars.hpp:
#ifndef VARS_H // Include guard to prevent multiple inclusion
#define VARS_H
void double_vars(int &, float &);
#endif
vars.cpp:
#include "vars.hpp"
void double_vars(int &x, float &y)
{
x *= 2;
y *= 2.0;
}
main.cpp:
#include <iostream>
#include "vars.hpp"
int main(void)
{
int a = 1;
float b = 2.2;
std::cout << "a: " << a << "; b: " << b << std::endl;
double_vars(a, b);
std::cout << "a: " << a << "; b: " << b << std::endl;
return 0;
}
Since x
and y
were declared as reference types (using the &
), writing to the expressions x
and y
in double_vars
is equivalent to writing to a
and b
in main
.
The C way of doing this would be with pointers instead of references:
vars.h:
#ifndef VARS_H // Include guard to prevent multiple inclusion
#define VARS_H
void double_vars(int *, float *);
#endif
vars.c:
#include "vars.h"
void double_vars(int *x, float *y)
{
*x *= 2;
*y *= 2.0;
}
main.c:
#include <stdio.h>
#include "vars.h"
int main(void)
{
int a = 1;
float b = 2.2;
printf("a: %d; b: %f\n", a, b);
double_vars(&a, &b);
printf("a: %d; b: %f\n", a, b);
return 0;
}
In this case, we're using pointers instead of references, so we need to pass the result of the expressions &a
and &b
to double_vars
; within the function, writing to the expressions *x
and *y
is the same as writing to a
and b
in main
.