Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c++: "double free or corruption" for global extern variable?

I'm interested in having a global variable one single time across the entire program. So I thought the best way to achieve this is to define it in the header file like so:

extern const std::string CONST_STR = "global string";

But that had lead to a "double free or corruption" run time error. Dropping the extern made the problem vanish.

  1. Could anyone explain this behavior?
  2. AFAIK, without the extern definition there would a CONST_STR per every translation unit, isn't there a way to get one entirely const global variable?
like image 342
Subway Avatar asked Mar 24 '23 09:03

Subway


1 Answers

Addressing the first part and additional questions about losing extern.

const std::string CONST_STR = "global string";

By C++ rules, this is identical to saying:

static const std::string CONST_STR = "global string";

If this is in an include file, you will create distinct strings in each translation unit (TU). They all work fine by themselves, but suppose you also add a function in the same header:

inline void foo() { std::cout << CONST_STR; }

If the << operator takes the string by const&, in each TU it will bind to a separate string. Thus violating the "one definition rule" and putting you to undefined behavior (UB). In practice it most likely works, but it is UB nonetheless.

The original extern form is similar to this, as the identical looking string literals are also separate in different TUs.

If you just say extern without an initializer, it's a declaration, and will be resolved by linker to the single definition. If you use an initializer, that makes it a definition. So again the object is created in each TU, but using a common public name, expecting other TUs to access it. The implementation is relieved from responsibility as you must ensure only one definition is actually provided.

The one definition rule is unfortunately too easy to break, and most of its forms explicitly permit the implementation to not issue any diagnostic. In practice the linker just picks a random definition from the pool. The double free is likely caused by emitting _atstart and _atexit entries for constructor and destructor calls, the object itself is melt into a single one, then gets as many construtor and destructor calls as you have TUs.

For the implementation it is all fair game, as for UB anything goes.

like image 148
Balog Pal Avatar answered Apr 02 '23 06:04

Balog Pal