Skip to content

Commit

Permalink
On the difference between global variables and global constants
Browse files Browse the repository at this point in the history
  • Loading branch information
cwecht committed May 13, 2023
1 parent 6d9cf87 commit 977c948
Showing 1 changed file with 105 additions and 0 deletions.
105 changes: 105 additions & 0 deletions docs/_posts/2023-05-13-globals.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
---
layout: post
title: "An inenarrable story about global constants"
date: 2023-05-13 20:21:00 +0100
---

## A plan falling apart

In C++ global variables can lead to several issues -- especially if
[defined in header files](https://stackoverflow.com/questions/19929681/c-global-variable-declaration).
Since we just introduced [nullopt]({% post_url 2023-04-30-nullopt %}) as a global variable in our
project, I expected to encounter such issues, which would have enabled me to talk about them here.

But that didn't quiet work out -- I didn't ran in such issues. As I found that quiet astonishing,
I'll tell you now what I had in mind at first -- and then why the issue I anticipated didn't occur.

## The story I wanted to tell you

Let's take for a moment this example below: there we have one header file and two implementation (`*.cpp`) files.

{% highlight cpp %}
// x.hpp
int x = 5;
{% endhighlight %}

In the header file `x.hpp` there is one variable `x` defined.

{% highlight cpp %}
// a.cpp
#include "x.hpp"
int main() {}
{% endhighlight %}

The two implementation files `a.cpp` and `b.cpp` just include this header file.

{% highlight cpp %}
// b.cpp
#include "x.hpp"
{% endhighlight %}

Let's now compile and link these files using the commands below.

{% highlight cpp %}
gcc -o a.o -c a.cpp
gcc -o b.o -c b.cpp
gcc a.o b.o
{% endhighlight %}

You don't need to do it manually right now; you can just have a look at it on
[compiler explorer](https://godbolt.org/#z:OYLghAFBqd5QCxAFwE4FN0BoCWIIDGAtgIYDW6AgqsAM4gDkAtACIDCAspQNICiA%2BgCEAqgEkAMi34AVAJoAFXgFIAzCwBK6ADYB1HMgQt0AI1EA7AGYB7BlgJWiABxxb0qcSTPAAriWDpRABMQAiUAJkFwwTtvWmQHAHlvZEdkgDEXdDMSInQUBHR%2BdAAPdAJkkmNXLAtM%2Blh0QP0rVCCQADYazLaARgAWLtds3JASADoCR0csUAZbHFpzAi1vQMbGAAZ52g4SHDMAZStvVAI8hh7thMcszdstT2A2uYBKLAhG5tbgnsGAn4ArH9hnlOOR0OIFshaGNkMVkDNGI5UFYAFZlZAQWgIEg3WgvJQbSiEyjEcH8Ij7HBEbxEfgYACO3hwGECEAAarx1AdRAkAHIAahUYwBBKJJJIgUCRVK5WQlVcWJxeKZJAwJIFmoF2Nx6FoAHoCCzlugJlMNVqdXj9RgCPKvK4zY4LZrSPsnWLiUT5TR0Mh%2BFp9mQAzhjKg1Tg9UrdbRVeqiVqBRYiMhAp7tksVmtggwtrgdntDsdTuc8wtrrdc/dHm0yRQ3h8mvFviAVH9egBOYE5PLGJ2Iub5zOrdZV/O7fZHE5nRiXfMVsx3LAPLzPBgNz7NtoDWquNphDbdkbFMYIKYDjNmZYjnNlguT4szi5XG6Lscrp459dvBZgijyFF0TtJdURAX4zHQAB3DI92CMIejeLRGABWw3zzKxGGkDB0AFcIVDnZA7heN4yBAeCRQADn6FQAHYKPadoVDCMIATCAYkIYPpUKXDDB3oQ9CKrBsYCgESICQew1l4JsWlwfBdyyHsQD/CEoRhOEEWsMxkAOAgSFcMCdysbThFodB5GKECwOXR4QlICgsDM1w7RwYz8CyQI2CsFYiEXX4PMhCC%2BVpYw3BAGisEcKxaH0VyzC8ny/Mi6LYuMwL0GCohQtQcLHO0DE4oOH1kAS2kkqcgrjKKtVkHSzLstyuIatK3zrKa1Bav2DKQrCmi3loR9zjrQpKTMalaXpdAmRZRoOS5Hl%2BSFEVPWRNEMWjPFPQlKUZTKCoqnQCADEKEo9vlA6XQFcZJmdBMtT7G700Q5DuLHDCVMhOJ1PhLBBK2YisAKSUwogEiwI2Q8OK4rAiDI4UKI2AEAQ2MJ2lYiiwg7FQgTQ2xeNsfjfqI94xIkqwpJk1A5IgBSQRAE8z2mLSdL0gz%2BhqYzkFM8zLLHUD2Ns0IIiiPLnOQOL3LMTzvLK6yAq6%2Bqwt%2BKKYvF4yWqSlXUrMOqepy34Kpcqrio1tr8qNw5it1rKlcck2Zdag2rYVvWwP6wbGH2ZABWKXC1AFAFVEEddlxerBcawDCGfPP7v0B9BgZy0HQ842xYfIjtEZojY%2BghxGegh7Hw54xgCZAATibgWAyYpr5qdppTrvPZndP0vJ2eZ7mLKszoPxCKIRcNtXFw%2BKXTf8qXrYagYteH8fktVuKp7CgYh8K%2B3ErNsX15q5ectXjfZad3eXZt/f3enc48P2a81lw5jo%2BdZiSRJL2BTdMxQdwmjIholgSRDhxFCxc3qjH7LHAGQM1hJ2eqnGGZEehjEzgCbOucIYAgLhsIuEd8aOXLkTISJN4DiRCOTdA0k654BppkOmD1m6c1bmzIyJkzLdz5q2GyXh%2B7CwiKLSqI8PLz3lkFV2YQF7a3nrPJep8GpiLXsbZqDtyrm2HtVDqe8yJ20UZvY%2B6iZFhTCBfEsjBr5XizDhcIYRH6WIAbA4BOCQB0OmBAt4UCQawOhrDJiYwVAURUBsCiWM%2BjtB6MjFBID0KlzwRXISYMVAdjGAeDsYQqIbAYn4iiNF2KMBUK9SJg4IFENgKTUhThMhUyofYMprgcrAB6PhLAawABuOAzjskjJBBcs5bC7mQG4egEBjBLmMPsNUABPbpWARnZFQGMhIxggKCTnFU3I2kEhmC0BMscaxSAOlLLYJotpxZNL1EuU6cpzhzi9toJcgYwzjK8msJcaBqTdLeBYB4dB2lQS6c%2BDm2lGHt2YVzVhvM8ygV%2BH3IWkReF3PqMnKwjhh70D4ioiWo9pY6KBMI7qZ9rJSPVkosCQICU630frPhFs1ElSJaEylqjnYiLxbomlWKtF6KZQ1BCv1sJuxTvYkuDAfb0SYO0PoApgAEAIAKepYwegCggFhTA998IvFsC4%2BOid8BxISUklJBd0m%2BKySnTxIBglyoRh2RiKDmLtBoskiJeMomE0KVXRAIBjgpGSNTKpzgal8iUgwYoIqxUSqlTK4Uc4W6syBf8kFPMrK/DQJgHAPwsCQXDI4N5/K8lOoYEkL13srAWGFe0UV4rJXStlfKiAXlqluBVQhdVRFXEJ2gdqk1acQAAnhj0TOKgxVhBojRJiATHWR2dfgjVpF4mJI2Mk1JhrMnZIYLk8duCNUcTCLmidBSW1YBOagGKbk%2BhAA%3D%3D).
However, if we do so we will encounter an linker error (`ld` being our linker in that case):

{% highlight bash %}
/usr/bin/ld: b.o:(.data+0x0): multiple definition of `x'; a.o:(.data+0x0): first defined here
collect2: error: ld returned 1 exit status
{% endhighlight %}

The reason for that is pretty much clear: by including `x.hpp` we introduced the definition of the same variable `x` in
two different [translation units](https://en.wikipedia.org/wiki/Translation_unit_%28programming%29). This means that we
now have two translation units which define the same globally visible variable. Hence, the two object files `a.o` and
`b.o` export each the exact same symbol for `x`. This is not allowed; therefore the linker report's an error.

The story I wanted to tell would have been:
1. I'd included `optional.hpp` in two `*.cpp` and linked them together.
2. We'd encountered a linker error stating that `nullopt` has been defined multiple times.
3. I'd told you how to fix that issue.

I did that -- and it just worked. No Linker whatsoever. I was surprised.

After a bit of fiddling around, going back and forth, I found the issue. It turns out that `nullopt` is *not* a global
*variable*. It is a global *constant*. If we go back to our example above and put a `const` right in front of `x`,
[the linker error
disappears](https://godbolt.org/#z:OYLghAFBqd5QCxAFwE4FN0BoCWIIDGAtgIYDW6AgqsAM4gDkAtACIDCAspQNICiA%2BgCEAqgEkAMi34AVAJoAFXgFIAzCwBK6ADYB1HMgQt0AI1EA7AGYB7BlgJWiABxxb0qcSTPAAriWDpRABMQAiUAJkFwwTtvWmQHAHlvZEdkgDEXdDMSInQUBHR%2BdAAPdAJkkmNXLAtM%2Blh0QP0rVCCQADYazLaARgAWLtds3JASADoCR0csUAZbHFpzAi1vQMbGAAZ52g4SHDMAZStvVAI8hh7thMcszdstT2A2uYBKLAhG5tbgnsGAn4ArH9hnlOOR0OIFshaGNkMVkDNGI5UFYAFZlZAQWgIEg3WgvJQbSiEyjEcH8Ij7HBEbxEfgYACO3hwGECEAAarx1AdRAkAHIAahUYwBBKJJJIgUCRVK5WQlVcWJxeKZJAwJIFmoF2Nx6FoAHoCCzlugJlMNVqdXj9RgCPKvK4zY4LZrSPsnWLiUT5TR0Mh%2BFp9mQAzhjKg1Tg9UrdbRVeqiVqBRYiMhAp7tksVmtggwtrgdntDsdTuc8wtrrdc/dHm0yRQ3h8mvFviAVH9egBOYE5PLGJ2Iub5zOrdZV/O7fZHE5nRiXfMVsx3LAPLzPBgNz7NtoDWquNphDbdkbFMYIKYDjNmZYjnNlguT4szi5XG6Lscrp459dvBZgijyFF0TtJdURAX4zHQAB3DI92CMIejeLRGABWw3zzKxGGkDB0AFcIVDnZA7heN4yBAeCRQADn6FQAHYKPadoVDCMIATCAYkIYPpUKXDDB3oQ9CKrBsYCgESICQew1l4JsWlwfBdyyHsQD/CEoRhOEEWsMxkAOAgSFcMCdysbThFodB5GKECwOXR4QlICgsDM1w7RwYz8CyQI2CsFYiEXX4PMhCC%2BVpYw3BAGisEcKxaH0VyzC8ny/Mi6LYuMwL0GCohQtQcLHO0DE4oOH1kAS2kkqcgrjKKtVkHSzLstyuIatK3zrKa1Bav2DKQrCmi3loR9zjrQpKTMalaXpdAmRZRoOS5Hl%2BSFEVPWRNEMWjPFPQlKUZTKCoqnQCADEKEo9vlA6XQFcZJmdBMtT7G700Q5DuLHDCVMhOJ1PhLBBK2YisAKSUwogEiwI2Q8OK4rAiDI4UKI2AEAQ2MJ2lYiiwg7FQgTQ2xeNsfjfqI94xIkqwpJk1A5IgBSQRAE8z2mLSdL0gz%2BhqYzkFM8zLLHUD2Ns0IIiiPLnOQOL3LMTzvLKsCIoCrr6rC34opi8XjJavyItV1KzDqnqct%2BCqXKq4rNbl0XKsOYr9ay5XHLNmXWp6CL2s6oKDbA/rBsYewzDiAV9mQAVilwtQBQBVRBHXZcXqwXGsAwhnzz%2B79AfQYGctB2PONsWHyI7RGaI2PoIcRnoIex%2BOeMYAmQAE4m4FgMmKa%2BanaaU67z2Z3T9Lydnme5iyrM6D8QiiEXjfVxcPil83/Kl22GoGHXp/n5K1bipewoGKfCsdxK2vyk3rZq7ect3g/ZaNm3Fc9vpvenc48P2a81lw5jk%2BdZiSRJIOBTdGYUGuEaKRBoiwEkMcOIoWrm9UY/ZU4AyBmsLOz1c4wzIj0MYhcATF1LhDAEFcNhVwTvjRy9ciZCRJvAcSIRyboGkm3PANNMh0wet3Tmvc2ZGRMmZYefNWw2S8OPYWERLYn0ltLQ%2BC9AjnzIhvXW69V5bzvnbHKYRxHT2qh1dee9TZn1UQ1DRbtdG3w9mosij8SyMBfleLMOFwhhC/o4yBaCYGkJAOw6YiC3jIJBmg6GsMmJjBUBRFQGwKJYz6O0HoyNcGwPQrXchDchJgxUB2MYB4OxhCohsBiYSKI0XYowFQr1EmDkQdQ2ApM6FOEyFTZh9g6muBysAHo%2BEsBrAAG44DOOySMkEFyzlsLuZAbh6AQGMEuYw%2Bw1QAE9hlYBmdkVAcyEjGCAoJOcTTcjaQSGYLQCyxxrFIA6Ustgmi2nFl0vUS5TpynOHOIO2glyBjDPMryawlxoGpMMt4FgHh0H6VBIZz4ObaS4f3HhXM%2BG8zzKBX4Y8haRDEW8%2Bo2crCOGnvQPix9p6SPXgrcxDUVYpTXk7JKyi0qGPtno0%2BOiKVHzFvvAxxLaVX2dg7Vl3ULEIV%2BthL2Od3E1wYCHeiTB2h9AFMAAgBABTtLGD0AUEAsKYA/vhF4tgfHp0zvgNJGSsk5Irvk0JRSc6BJANExVCMOyMVwcxdoNFskJLxkkwmlSm6IBAMcFIyRqZNOcC0vkSkGDFHFZK6Vsr5XCjnD3VmULwUwp5lZX4aBMA4B%2BFgSC4ZHB/KFWU11DAki%2BuDlYCwYr2gSqlTKuVCqlUQC8s0tw6qEJaqIr4jOKC9XmrziAAE8MeiFxUJKsINEaJMQiS6xObqKHatIukzJGxsm5JNYU4pDBSlTrIdqjiYQC3Toqe2rANzUAxTcn0IAA).

## The particularities of global constants

But why is that? Why behave global *variables* different than global *constants*?
Well' it turn out, that they do not only differ in their mutability. They also
differ in their *linkage*.
Global *variables* have *external linkage*-- global *constants* have *internal linkage*.

What is now *linkage*? To put it simple, the *linkage* of a symbol defines whether the
symbol is visible outside of the translation unit or not. A different way to put it is,
that symbols with *external linkage* are visible to the linker, symbols with
*internal linkage* are not.

[The C++ Standard
defines](https://stackoverflow.com/questions/6173872/why-is-multiple-definition-of-a-const-global-variable-allowed-in-c-and-not-in)
that "constants on namespace level have internal linkage*. The constants/variables we are looking at right now are
defined in the global namespace, so this rule applies here. But it would also hold if we'd put the constants in another
namespace.

So the gist of this is: as `nullopt` is a *constant* defined in the *global namespace* it is not visible to the linker
and can therefore not cause any linker errors.

## Conclusion

C++ can be surprising. That's nothing new, but holds true especially in this case. Today we learned about the
differences between global *variables* and global *constants* as well as the difference between *external*
and *internal* linkage. On the topic of *linkage* in can recommend
[this blog post](http://www.goldsborough.me/c/c++/linker/2016/03/30/19-34-25-internal_and_external_linkage_in_c++/)
which covers the topic in much more detail.

After this small detour we can (hopefully) proceed with our journey towards a feature complete optional in the next
post.

0 comments on commit 977c948

Please sign in to comment.