Global variables in comparison to #define

Started by
27 comments, last by Zoner 11 years, 9 months ago
As far as I can understand, if you use the pre processor command #define, for example, #define money 100, it'll replace all the instances of "money" and replace them with 100. As far as I know it is global and can be used all over your code?

What is the point of using #define instead of a global variable? Don't they pretty much do the same thing?
What's the difference between the two?
The majority of Internet Explorer users don't understand the concept of a browsing application, or that there are options.
They just see the big blue 'e' and think "Internet". The thought process usually does not get much deeper than that.

Worms are the weirdest and nicest creatures, and will one day prove themselves to the world.

I love the word Clicky
Advertisement
Global variables respect namespaces. For example:

[source]
int x;

struct s
{
char x;
};

void f(s v)
{
v.x = 23; // okay
}

#define x 100

void g(s v)
{
v.x = 34; // gets converted to v.100 by preprocessor, not good
}
[/source]

This silly example is just that, but try working with an API that makes heavy use of the preprocessor, for example Win32 API, and you soon find the problems.

Another point of course is that the compiler can produce more meaningful error messages with proper variables. And any decent compiler will generate the same code for a const T t as a #define.

There's no reason really to use the preprocessor for anything except including files, conditional compilation and weirdo stuff like __LINE__ and __FILE__ macros these days. Rule of thumb - if it is possible to do it without the preprocessor, do it the other way.
A #define macro is more akin to a global constant than a global variable. For instance, this code won't compile:

#define MONEY 100
int main () {
MONEY = 50;
}


Whereas this is totally legit:

int MONEY = 100;
int main () {
MONEY = 50;
}



To get similar to a define you want something like this:

const int MONEY = 100;
int main () {
MONEY = 50; // oops, won't compile (but is more useful error than the #define version, try it!)
}



#define basically creates a text substitution in your code, like a programmable find and replace. It is handy when you want to do precisely that - replace one bit of text with another. It is dangerous for many reasons, some of which are covered here for example.


In general, you should prefer constants to macros.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

I'm assuming you mean C++.

A define statement replaces -any- instance of "money" in your code, no matter if it's a variable or function in any namespace or scope. You get no namespace and the possibility of name clashes is pretty much guaranteed unless you give it a very long and unique name like "MYPROJECT_MONEY". A const global can be overridden in a scope defining another instance of "money" and you can even put it in a specific namespace avoiding other files declaring the same name.

Defines are uncontrollable and will find a way of leaking into places where you don't want it unless you give them very specific and ugly names. The windows.h header is a great example of this.. you better define LEAN_AND_MEAN before using it and hope all the defines really get undefined.

They're only "global" in the sense that if you include a file with it, you'll include the define it contains as well. But the same goes for globally defined const values, so there's no difference there.
So basically you're all saying that if I can use a global constant rather than a #define, I should?
The majority of Internet Explorer users don't understand the concept of a browsing application, or that there are options.
They just see the big blue 'e' and think "Internet". The thought process usually does not get much deeper than that.

Worms are the weirdest and nicest creatures, and will one day prove themselves to the world.

I love the word Clicky

So basically you're all saying that if I can use a global constant rather than a #define, I should?

You can only benefit by doing so. Const values can be defined in headers as well. If you need a global variable (god forbid) you'll have to use the extern keyword in the header and define it in an implementation file.
define is just a text replace, and does not care about the language. that can lead to some interesting abuses, and some interesting uses (the header include once thing). other than that, use language features, as they don't want to bite you in the back.

like #define max did all the time for me...

if you don't plan to ctrl-r replace-all-text, don't use #define.
If that's not the help you're after then you're going to have to explain the problem better than what you have. - joanusdmentia

My Page davepermen.net | My Music on Bandcamp and on Soundcloud

Preprocessor macros can be useful for many things, but I would not use them for constant values because you lose type safety and conflicts are possible that could create really hard to find bugs.
The above advice is all perfectly valid and I don’t want my post to be misunderstood as a way to “get around” these faulty macro points—there is no real substitution for inlined functions etc.

I just want to add some safety tips for those few times when you really do need a macro.

  1. Naming macros such as “MONEY” is too generic. Due to the consequences of text replacement, you could end up with some very abstract and hard-to-trace errors if you use too-general names for your macros. The best way to combat this is to add a fake namespace to your macro. For example, in my engine there are 16 projects each with one namespace. lse, lss, lsm, lsg, etc. Within those projects, I replicate the namespaces within the macros. LSE_ELEMENTS( VAR ), LSG_OPENGL, LSG_DIRECTX11, etc.
  2. The above not only reduces conflicts but also lets know you 2 things: #1: Is this macro from my own library?, and #2: Which library? LSG_ = L. Spiro Graphics library. Easy.
  3. #undefine macros as soon as they are no longer needed. Header guards etc. should never be undefined, but within translation units (.cpp files) you might have some macros inside functions to make some tasks easier. An example in my engine is “#define LSG_HANDLE2CBUF( HANDLE )”, which, in DirectX 11 and DirectX 10, translates my custom handle into a custom cbuffer pointer, and is used only inside the CDirectX11CompiledShader and CDirectX10CompiledShader .CPP files. It is considered tidy to clean up after yourself, so #undef at the bottom of the .CPP files is a good idea. I have heard rumors of the possibility of macros “leaking” from one translation unit into another under some compilers so this is a good idea in general to avoid bugs.
  4. __ (2 underscores) is a prefix reserved for the system/compiler. If you want to make absolutely sure your macros will never conflict with anything, you could add some underscores in front, but make sure it is not just 2 underscores. At work we use 3.



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


__ (2 underscores) is a prefix reserved for the system/compiler. If you want to make absolutely sure your macros will never conflict with anything, you could add some underscores in front, but make sure it is not just 2 underscores. At work we use 3.


Anything starting with two underscores or one underscore and a capital letter is reserved for the compiler. So, anything starting with three underscores is reserved (since it also starts with two underscores) and any capitalized macro that starts with any underscores is reserved. Also, there can't be any sequence of two underscores in the identifier, even if it's not at the start. The compiler is not likely to define a macro that starts with three underscores but it is still allowed to do so.

This topic is closed to new replies.

Advertisement