-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
On the difference between global variables and global constants
- Loading branch information
Showing
1 changed file
with
105 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |