2

I have encountered the following strange situation: I have managed to have a global static variable with two instances ... Is this normal or is this a bug in the compiler or is this a hidden realm of C++? The following reproduction is an extract from a larger project (where the behaviour is the same), obviously the names were changed to protect the culprit (and yes, I know there are memory leaks in this code).

Here comes the code:

// other.h
#ifndef _OTHER_H_
#define _OTHER_H_

struct other
{
    long longer;
    int inter;
    char charer;
};

void dosomething();

#endif

And

// other.cpp
#include "other.h"
#include "util.h"

void dosomething()
{
  other* something = alloc_mem(other, 4);
}

And

// util.h
#ifndef _UTIL_H_
#define _UTIL_H_

#include <memory.h>
#include <string>

#include "test_class.h"

template <class T> T* allocate(size_t count, const char* f, long l, const char* sth)
{
    T* tmp = new T[count];
    memset(tmp, 0, count * sizeof(T));
    test_class<T*>::instance().throwIn(tmp, f, l, sth, count);

    return tmp;
}

#define alloc_mem(type,count) allocate<type>(count, __FILE__, __LINE__, (char*)0)

#endif

And

// main.cpp
#include "other.h"  
#include "util.h"

int main()
{
  int* i = alloc_mem(int, 1);
  int* i1 = alloc_mem(int, 20);
  char* c = alloc_mem(char, 1);

  dosomething();
  int* i3 = alloc_mem(int, 1);
}

And the main part:

// test_class.h
#ifndef test_class_H
#define test_class_H
#include <stdlib.h>
#include <iostream>
#include <typeinfo>
#include <cxxabi.h>

static long int all_alloc = 0; // THIS will get linked in two times!

template <typename T>
class test_class
{
private:
    test_class() {}
    static test_class<T>* pinstance;

public:
    ~test_class() {}
    static test_class& instance() {
        if(pinstance == NULL) {
            pinstance = new test_class();
        }
        return *pinstance;
    }

    void throwIn(T item, const char* file, long line, const char* _compiler, long count) {
        int status;
        char* s = abi::__cxa_demangle(typeid(T).name(), 0, 0, &status) ;
        std::cout << "request:" << sizeof(T) * count << " bytes, type:" << s << " @ "<< 
                 file << ":" << line << " global_addr:" << &all_alloc << std::endl;
        all_alloc += sizeof(T) * count ;
        free(s);
        std::cout<<"All memory:" << all_alloc << std::endl;
    }
};

template <class T> test_class<T>*  test_class<T>::pinstance = NULL;

#endif

So, you have to compile this as:

g++ main.cpp other.cpp -o test

and the run it, and:

$ ./test
request:8 bytes, type:int* @ main.cpp:6 global_addr:0x6022d8
All memory:8
request:160 bytes, type:int* @ main.cpp:7 global_addr:0x6022d8
All memory:168
request:8 bytes, type:char* @ main.cpp:8 global_addr:0x6022d8
All memory:176
request:32 bytes, type:other* @ other.cpp:6 global_addr:0x6022f8
All memory:32
request:8 bytes, type:int* @ main.cpp:11 global_addr:0x6022d8
All memory:184

so, as I could see with a pretty big surprise, I have two global addresses for all_alloc... Indeed, nm -C test shows:

00000000006022d8 b all_alloc
00000000006022f8 b all_alloc

So, obviously the questions:

Why? How is this possible? Is there something allowing this kind of behaviour or is this a bug somewhere in the compiler or linker?

Ferenc Deak
  • 34,348
  • 17
  • 99
  • 167
  • This "test_class.h" directly above the static you question is a fairly strong indicator this is in a header, and your command line suggests two source files are including it, each getting their own `all_alloc`. Each file that includes `test_class.h` is going to get their own `all_alloc`. – WhozCraig Nov 21 '13 at 20:45
  • This isn't the problem, but names that begin with an underscore followed by a capital letter (`_OTHER_H_`) and names that contain two consecutive underscores are reserved to the implementation. Don't use them. – Pete Becker Nov 22 '13 at 18:57

2 Answers2

6

In this case, I don't think you want to declare your all_alloc static. In this case, it means that the linkage is internal. In your case, you get two copies since two files include the header.

If you remove static, then you will have two copies that clash, and result in a linker error. Which is not good.

I believe what you want to do is to change static to extern, then in one cpp file, define the variable and its value.

header:

extern long int all_alloc;

cpp:

long int all_alloc = 0;

This will provide one copy of all_alloc that is shared among your code.

crashmstr
  • 28,043
  • 9
  • 61
  • 79
0

When you have global variable it already has static duration, so static keyword means something different for it. As you have class definition in the file you define your global static variable I assume that is a header. Why it is bad idea to use static global variable in header and what you will get you can read here So answer is: it is expected behavior and not a bug.

Community
  • 1
  • 1
Slava
  • 43,454
  • 1
  • 47
  • 90