Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Value initialized before main() disappears

Tags:

c++

Can not understand weird program behavior - hopefully someone can explain.

dummy.h:

#ifndef DUMMY_H
#define DUMMY_H

#include <iostream>

class Dummy
{
    int val;

public:
    int Init(int new_val)
    {
        return val = new_val;
    }

    int Get()
    {
        return val;
    }

    Dummy():
        val(-1)
    {
        std::cout << "constructed" << std::endl;
    }

    ~Dummy()
    {
        std::cout << "deconstructed" << std::endl;
    }
};

#endif /*DUMMY_H*/

header.h:

#include "dummy.h"

extern Dummy dummy;

dummy.cpp:

#include "dummy.h"

Dummy dummy;

main.cpp:

#include <iostream>

#include "header.h"

int res1 = dummy.Init(2);
int res2 = dummy.Get();

int main()
{
    std::cout << res1 << std::endl;
    std::cout << res2 << std::endl;
    std::cout << dummy.Get() << std::endl;

    return 0;
}

compiling: g++ -Wall -Wextra main.cpp dummy.cpp

output:

constructed
2
2
-1
deconstructed

why the 2nd Get(), called in main() function returns -1? Why the assigned value disappears from the dummy instance and no deconstructor was called. How it becomes -1?

upd: added debug info to Init() and Get():

new output:

init
get
constructed
2
2
get
-1
deconstructed

upd2: funny fact - separate compiling and linking object files in one executable changed the situation:

g++ -c dummy.cpp 
g++ -c main.cpp 
g++ dummy.o main.o 

./a.out

constructed
init
get
2
2
get
2
deconstructed

but IT IS A TRAP!

like image 296
ShPavel Avatar asked Sep 18 '13 11:09

ShPavel


2 Answers

The order of initialization of global variables in different translation units is unspecified. Either dummy or the two global ints in main.cpp could be constructed first.

In your case, it's res1 and res2 that got initialized first, so you invoked undefined behaviour by calling member functions on object that wasn't yet constructed.

To fix it, try this:

header.h:

#include "dummy.h"

Dummy& getDummy();

dummy.cpp:

#include "dummy.h"

Dummy& getDummy()
{
    static Dummy dummy; // gets initialized at first call
                        // and persists for the duration of the program
    return dummy;
}

main.cpp

int res1 = getDummy().Init(2);
int res2 = getDummy().Get();

If it helps understanding, add some debug cout statements to Get and Init, too. What you experienced is also known as static initialization order fiasco.

like image 55
jrok Avatar answered Oct 11 '22 16:10

jrok


So, what happens is that int res1 = dummy.Init(2); and int res2 = dummy.Get(); is run BEFORE dummy has been initialized. Then, before main is entered, dummy gets constructed and has the value -1 stored in val.

Yyou can possibly change this in this particular case, for this version of the compiler, by rearranging your object files to g++ -o myprog dummy.o main.o instead of g++ -o myprog main.o dummy.o - but that will not GUARANTEE to fix the problem, and in a future version of compiler/linker, the result may well alter again. The C++ standard gives no guarantee at all - I'm just suggesting the "order may matter" from what I've personally seen. And since there is no particular requirement for these things, the compiler vendor is allowed to change it in any which way they like at any time.

like image 28
Mats Petersson Avatar answered Oct 11 '22 16:10

Mats Petersson