Reputation: 313
I was playing a bit with external variables and was wondering why I cannot access an external structure from outside functions?
Here's my code:
a.h
struct testing {
unsigned int val;
const char* str;
testing(unsigned int aVal, const char* aStr) : val(aVal), str(aStr){};
}
extern testing externalStruct;
a.c
#include "a.h"
testing externalStruct(10, "test");
test.c
#include <iostream>
#include "a.h"
unsigned int valCopy = externalStruct.val;
const char* strCopy = externalStruct.str;
int main()
{
std::cout<<"Direct val : "<<externalStruct.val<<std::endl; // Working, print 10
std::cout<<"Direct str : "<<externalStruct.str<<std::endl; // Working, print "test"
std::cout<<"Copy val : "<<valCopy<<std::endl; // Print 0 instead of 10
std::cout<<"Copy str : "<<strCopy<<std::endl; // Print nothing
return 0;
}
Any idea?
Upvotes: 2
Views: 256
Reputation: 6875
The order of initialization of the global objects is not defined.
In this case I guess that valCopy
and strCopy
were initialized before the constructor of externalStruct
was invoked (=> initialized with junk).
If you put the initialization of valCopy
and strCopy
into the main
body I believe everything will work properly:
int main()
{
unsigned int valCopy = externalStruct.val;
const char* strCopy = externalStruct.str;
std::cout<<"Direct val : "<<externalStruct.val<<std::endl; // Working, print 10
std::cout<<"Direct str : "<<externalStruct.str<<std::endl; // Working, print "test"
std::cout<<"Copy val : "<<valCopy<<std::endl; // ??? did it work?
std::cout<<"Copy str : "<<strCopy<<std::endl; // ??? did it work?
return 0;
}
EDIT
More info is here "C++ global initialization order ignores dependencies?"
Upvotes: 0
Reputation: 6063
You are being tricked by the static initialization order fiasco - One of the shortcomings of C++.
Both
unsigned int valCopy = externalStruct.val;
const char* strCopy = externalStruct.str;
and
testing externalStruct(10, "test");
are (and need to be) actually called before main()
is executed. Unfortunately, C++ has no language construct that allows you to express in what order the initialization should be done - this is more ore less randomly decided by the compiler - In your case, the first block is apparently executed before the second, leading to the fact that externalStruct
has not been initialized yet when you copy values from there into valCopy
and strCopy
.
You can work around this language deficiency by wrapping the initializations into functions that return a statically initialized value - This gives you control over the order those initializations are done.
a.c:
testing &x() {
static testing *t = new testing(10, "test");
return *t;
}
test.c
...
valCopy = x().val;
strCopy = x().str;
...
Upvotes: 1
Reputation: 49986
This problem is caused by the fact that order of initialization of static (global) variables is unknown. It even has a name static initialization order fiasco. This means that global variables from test.c translation unit were initialized before global variables from a.c transation unit.
The usual solution is to use function with static variable. When function is called then static variable (during first use) is initialized. With c++11 initialization of such static function local variables is thread safe.
Solution for your code could look as follows:
a.h
//...
testing& GetExternalStruct();
a.c
//...
testing& GetExternalStruct() {
static testing externalStruct(10, "test");
return externalStruct;
}
test.c
unsigned int valCopy = GetExternalStruct().val;
const char* strCopy = GetExternalStruct().str;
Upvotes: 2