Global variables in comparison to #define

Started by
27 comments, last by Zoner 11 years, 9 months ago
one important difference is that global variables are really variables, the compiler has to assume that you could change them, even if they are const (e.g. modifying the binary or unlocking that segment via system calls, changing a value and locking that segment again). That means that you have a load, possibly a cache miss and in cases where that value is just 0 or 1, the compiler could optimize code, but with variables it's not fully legal.

one way around this is to use enums

enum{DEBUGOUTPUT_ENABLED=0};

...
void foo()
{
if(DEBUGOUTPUT_ENABLED)
..
}


doesn't work for floats, obviously, or any non primitive type, but for cases where you want to replace defines, it can make quite sense to use enums.
Advertisement
To the best of my knowledge modern compilers will try to inline const variables into the code whenever possible. There are few cases where you actually need a physical variable, for example in a case like this:
const int myConst = 42;
...
myFunction(&myConst);
where the signature is void myFunction(const int* value).

Edit: I just tried something like this for kicks:
[source lang="cpp"]const int myConst = 10;
[...]
*const_cast<int*>(&myConst) = 5;

for (int i = 0; i < myConst; ++i)
std::cout &lt;&lt; i &lt;&lt; " ";
[/source]
It just crashes in MSVC Release builds. And in http://stackoverflow...alue-of-a-const Andrew Khosravian claims to quote the standard which would reinforce my opinion.
as I said, you need to unlock that segment via system function, I did that like 10years ago on Win95 if I recall correctly, to change const strings during startup time.

note also, that if you take the address, you actually need to make it static const, just naming it const would result in a different address for every translation unit, unless you have just one uber/unity file, it might cause you a lot of trouble. (e.g. if you push the address of a 'stop' flag into a queue and check for it in another translation unit).

afaik, enums are rvalues, you cannot have those kind of misstakes.

I also suggest to not overgeneralize one compiler and platform as a proof of something, if you compile for e.g. ARM for gameboy advance, and you have a const int which has more than 24bit set (e.g. 0xffffffff), then there is no instruction to create that 0xffffffff in one cycle, the compiler starts to be 'smart' and loads that data from a global address space, which usually cost you more than those instructions that are needed to create the 0xffffffff by computation, an enum can help (even then compilers sometimes try to cluster those).

You're right that the const will be embeded, on an x86, tho :)
So basically we can agree on saying "using a const is fine using a modern compiler on a desktop plattform and probably quite a few others". Because if we move into the weirder corners you need extremely domain-specific knowledge anyway and cannot make any generalizations because the intersection of what you should be using on consoles, desktop and some of the embedded devices you encounter is basically a very primitive C no one would like to work with if they had any choice?
or we can agree enums are always fine

whatever you prefer
Unless you need a non-integer type constant.

Edit: And we can agree that what originally prompted me to my response is true too, meaning

one important difference is that global variables are really variables, the compiler has to assume that you could change them, even if they are const (e.g. modifying the binary or unlocking that segment via system calls, changing a value and locking that segment again). That means that you have a load, possibly a cache miss and in cases where that value is just 0 or 1, the compiler could optimize code, but with variables it's not fully legal.

is pretty much untrue. The compiler does not have to create 'real variables'. The compiler does not have to assume you change them, changing them is undefined behavior and may or may work depending on the compiler. The compiler can optimize the variable completely out, it is completely legal.

I'm leaving you with "enums might be better if you are sticking to integer types anyway and might work on some embedded platforms". I do not have a problem with the end result in this case (though I still prefer the more idiomatic constants which work fine on my target platforms) but I highly object to the plainly wrong information we encountered on the way.
The ultimate answer is:
Whenever there is a way to get the same result without defines, that solution is always preferred.
If not, defines are there to cover your back.

End of discussion.


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid


is pretty much untrue. The compiler does not have to create 'real variables'. The compiler does not have to assume you change them, changing them is undefined behavior and may or may work depending on the compiler. The compiler can optimize the variable completely out, it is completely legal.


Yep. Writing to a variable that was declared const then has its const-ness removed with const_cast or a more basic C style cast is undefined behaviour.

If, however, you take the address of a const T, then the compiler can no longer inline it I believe. But you couldn't take the address of a #define literal anyway so this is apples and orangutans.
Braindump inc:

  • 'C' programs or C++ written 'like C' should prefer using enums for constants.
  • Global variables are almost always re-read from memory. If they are pointers, the pointers are re-loaded and then dereferenced on almost every access. This can be a perf problem, even when the pointer is just some virtual base class. Compilers treat globals as volatile for the most part.
  • global const variables should be externed and placed in a single file. This also means the compiler can't inline their value in the code like it can with an enum or a define. On the plus side they are visible in mapfiles and debuggers and easy to change even in optimized and release builds.
  • If you make the variable static in some attempt to make the compiler inline its value where it is used, you will generally fail, and the linker map won't show the symbol (it is static), and it will take up space in your executable for every .cpp file that includes your header (which can be pretty bad if your static is a big array of bytes or something large).
  • class member static const variables are ok to use, they work more like enums (but can't be anything but integer types until C++11 is supported on your environment)
http://www.gearboxsoftware.com/

This topic is closed to new replies.

Advertisement