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.
extern
definition there would a CONST_STR per every translation unit, isn't there a way to get one entirely const global variable? 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With