From 977c948690aa15f336d08d7dd006c9b29549a50f Mon Sep 17 00:00:00 2001 From: Christopher Wecht Date: Sat, 13 May 2023 21:23:20 +0200 Subject: [PATCH] On the difference between global variables and global constants --- docs/_posts/2023-05-13-globals.md | 105 ++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 docs/_posts/2023-05-13-globals.md diff --git a/docs/_posts/2023-05-13-globals.md b/docs/_posts/2023-05-13-globals.md new file mode 100644 index 0000000..9b26eae --- /dev/null +++ b/docs/_posts/2023-05-13-globals.md @@ -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.