2

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?

user694733
  • 15,208
  • 2
  • 42
  • 68
gagou7
  • 293
  • 3
  • 12

3 Answers3

2

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;
marcinj
  • 48,511
  • 9
  • 79
  • 100
1

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;
... 
tofro
  • 5,640
  • 14
  • 31
0

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?"

Community
  • 1
  • 1
Alex Lop.
  • 6,810
  • 1
  • 26
  • 45
  • I was thinking that it may use some sort of lazy loading like for call to external function. The program knows that we want to access the symbol "externalStruct", why is it not able to initialize it before making a copy of the value? – gagou7 Jun 09 '16 at 08:51
  • @gagou7 The point is there is no such thing as "Program" that knows... Program is something that you write, then it passes the compilation and linkage. Since the declaration and initialization of the these global variables is done in different compilation units (different files), the compiler cannot force the order you wish. More details are here: http://stackoverflow.com/a/3746249/5218277 – Alex Lop. Jun 09 '16 at 10:49