Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ static variable multiple instances

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?

like image 689
Ferenc Deak Avatar asked May 13 '26 00:05

Ferenc Deak


1 Answers

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.

like image 99
crashmstr Avatar answered May 14 '26 14:05

crashmstr



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!