2

I have seen a lot of programs using #define at the beginning. Why shouldn't I declare a constant global variable instead ?

Iharob Al Asimi
  • 52,653
  • 6
  • 59
  • 97
Sourav Kanta
  • 2,727
  • 1
  • 18
  • 29

3 Answers3

10

(This is a C++ answer. In C, there is a major advantage to using macros, which is that they are pretty much the only way you can get a true constant-expression.)


What is the benefit of using #define to declare a constant?

There isn't one.

I have seen a lot of programs using #define at the beginning.

Yes, there is a lot of bad code out there. Some of it is legacy, and some of it is due to incompetence.

Why shouldn't I declare a constant global variable instead ?

You should.

A const object is not only immutable, but has a type and is far easier to debug, track and diagnose, since it actually exists at compilation time (and, crucially, has a name in a debug build).

Furthermore, if you abide by the one-definition rule, you don't have to worry about causing an almighty palaver when you change the definition of a macro and forget to re-compile literally your entire project, and any code that is a dependent of that project.

And, yes, it's ironic that const objects are still called "variables"; of course, in practice, they are not variable in the slightest.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • 1
    Yup, this. You should use `const` in preference to `#define` every time it is capable of being a replacement. I would note, however, that `const` variables technically are not globals but module-scoped. – Jack Aidley Jun 02 '15 at 13:12
  • @JackAidley: That's not true, though [it _is_ true that _by default_ a `const` object declared at namespace scope has internal linkage](http://stackoverflow.com/a/998457/560648). But you can still `extern` it to your heart's content. As such, there is nothing inherently "non-global" about `const` objects. – Lightness Races in Orbit Jun 02 '15 at 13:14
  • @Lightness Races in Orbit. `#define SETBITS(s,bv,m) (((s) & ~(m)) | ((bv) & (m)))` why should I use something like this as function? (I speak about C, but I think also C++) – Sir Jo Black Jun 02 '15 at 13:15
  • Technicality accepted. – Jack Aidley Jun 02 '15 at 13:15
  • @SergioFormiggini: You shouldn't. Granted, I really am focusing on C++, in which you can `constexpr` the crap out of that and get the same result with none of the downsides. I'm not qualified to outright state that the benefits of an inline function outweigh potential performance concerns in C. Frankly, though, I'd be astounded. – Lightness Races in Orbit Jun 02 '15 at 13:16
  • @SergioFormiggini: `SETBITS(s, bv, m++)`. – Jack Aidley Jun 02 '15 at 13:16
  • @Jack Aidley. What do you mean? – Sir Jo Black Jun 02 '15 at 13:17
  • @SergioFormiggini He's pointing out that while `SETBITS(s, bv, m++)` looks legitimate, it will actually cause problems due to it being a macro rather than a function. – JorenHeit Jun 02 '15 at 13:18
  • 1
    @SergioFormiggini: If you call `SETBITS` in the manner Jack showed... actually, let's change that a bit to make it clearer. Call it as `SETBITS(x,y,z++)`. Now, since that third argument (called `m` inside the definition) is evaluated twice inside the macro, you end up with `z++` evaluated twice, which is almost certainly not what you wanted. Macros are full of pitfalls like this. Sure, you can work around them and avoid them, but then why bother with the pain in the first place? Just write a proper function and be done with it. – Lightness Races in Orbit Jun 02 '15 at 13:18
  • @LightnessRacesinOrbit You spoiled the exercise for him! ;-) – JorenHeit Jun 02 '15 at 13:18
  • @JorenHeit: I know :P – Lightness Races in Orbit Jun 02 '15 at 13:19
  • Yes, but is not the matter of my question. The matter is that if I use a call for that code the compiler should not optimize the code. Then, If an user uses a #define such that have to understand the issues it may create. Furthermore the use of that macro hardly should use m++ .... – Sir Jo Black Jun 02 '15 at 13:21
  • All literals have a type. It is impossible to `#define` a constant which isn't a literal and therefore all `#defines` do have a type. It is just less obvious which type they are. – Lundin Jun 02 '15 at 13:22
  • 1
    This answer is not correct, since it is a C++ only anwer. The question is also tagged C, co a correct answer should take the completely different situation for C and for code that interfaces both languages into account. – Jens Gustedt Jun 02 '15 at 13:22
  • @Lundin: The name `SETBITS` does not have a type. Of course the expression to which it ultimately evaluates does. – Lightness Races in Orbit Jun 02 '15 at 13:22
  • The type doesn't care in that contest. Is a macro to set bits with a mask ... the user should know the bit operations are better accomplished using unsigned int ... If you pass something different you'll have (probably) undefined behaviour. But that code has a good chance of very good optimization. – Sir Jo Black Jun 02 '15 at 13:25
  • @LightnessRacesinOrbit "It is impossible to `#define` a constant which isn't a literal". Function-like macros is another thing entirely, they are just icky text replacement with no type safety. But the question is about the difference of defined constants and const variables, not about the difference between function-like macros and functions. – Lundin Jun 02 '15 at 13:28
  • @LightnessRacesinOrbit because it uses properties of `const` qualified variables that only hold for C++ and not for C. As you say, C doesn't seem to be your strong point, so don't claim something for it. – Jens Gustedt Jun 02 '15 at 13:28
  • @JensGustedt: I added a notice to the answer following our discussion under the question. That still doesn't make this answer flat-out _wrong_. – Lightness Races in Orbit Jun 02 '15 at 13:29
  • @JensGustedt: Wow, no need to attack my credentials/skills. I missed one fact about a complex language. How rude. – Lightness Races in Orbit Jun 02 '15 at 13:29
  • Ops, I didn't read the question was about constant ... :) – Sir Jo Black Jun 02 '15 at 13:33
  • @LightnessRacesinOrbit, let's state it like that, you missed half of the answer for the question and insisted for quite a while that this is correct. I found *that* rude, so we are even. – Jens Gustedt Jun 02 '15 at 13:34
  • @Jens: Good grief. Now you're bandying about the accusations, but I notice you cast the first close-vote, shutting down this question as an "exact duplicate" of one _that focuses only on C_. What's that, you say? "Only half the question"? Yes, indeed; your rabid anti-C++ biases are showing. – Lightness Races in Orbit Jun 02 '15 at 13:35
  • @JensGustedt Pardon my ignorance, but what exactly are the C++ specific properties of `const` qualified variables mentioned in this anwer? – JorenHeit Jun 02 '15 at 13:44
  • @JorenHeit: http://stackoverflow.com/questions/30596943/what-is-the-benefit-of-using-define-to-declare-a-constant/30597115?noredirect=1#comment49263324_30596943 (he's not wrong) – Lightness Races in Orbit Jun 02 '15 at 13:50
  • @LightnessRacesinOrbit Thanks, I'll look into it. I wasn't suggesting he's wrong by the way. Just interested :-) **Edit:** seems like you accidentally pasted the link to this very question... **Edit2:** whoops got it. Sorry – JorenHeit Jun 02 '15 at 13:54
  • @LightnessRacesinOrbit, yes I voted to close it, but not because it was a duplicate. The SO attributes my close reason to the "majority" reason, a recent change in the system that I still find a bit weird. – Jens Gustedt Jun 02 '15 at 19:07
4

What is the benefit of using #define to declare a constant?

Declaring a constant with #define is a superior alternative to using literals and magic numbers (that is, code is much better off with a value defined as #define NumDaysInWeek (7) than simply using 7), but not a superior alternative to defining proper constants.

You should declare a constant instead of #define-ing it, for the following reasons:

  • #define performs a token/textual replacement in the source code, not a semantic replacement.

    This screws up namespace use (#defined variables are replaced with values and not containing a fully qualified name).

    That is, given:

    namespace x {
    #define abc 1
    }
    

    x::abc is an error, because the compiler actually tries to compile x::1 (which is invalid).

    abc on the other hand will always be seen as 1, forbidding you from redefining/reusing the identifier abc in any other local context or namespace.

  • #define inserts it's parameters textually, instead of as variables:

    #define max(a, b) a > b ? a : b;
    
    int a = 10, b = 5;
    int c = max(a++, b); // (a++ > b ? a++ : b); // c = 12
    
  • #define has absolutely no semantic information:

    #define pi 3.14 // this is either double or float, depending on context
    
    /*static*/ const double pi = 3.14; // this is always double
    
  • #define makes you (the developer) see different code than the compiler

    This may not be a big thing, but the errors created this way are obscure, unexpected and waste a lot of time (you could look at an error, where the code looks perfectly fine to you, and curse the compiler for half a day, only to discover later, that one of the symbols in your expression actually means something completely different).

    If you get with a debugger to code using one of the declarations of pi above, the first one will cause the debugger to tell you that pi is an invalid symbol.

Edit (valid example for a local static const variable):

const result& some_class::some_function(const int key) const
{
   if(map.count(key)) // map is a std::map<int,result> member of some_class
       return map.at(key); // return a (const result&) to existing element
   static const result empty_value{ /* ... */ }; // "static" is required here
   return empty_value;  // return a (const result&) to empty element
}

This shows a case when you have a const value, but it's storage needs to outlast the function, because you are returning a const reference (and the value doesn't exist in the data of some_class). It's a relatively rare case, but valid.

utnapistim
  • 26,809
  • 3
  • 46
  • 82
  • Don't do `static const`! You should always, always write `const double pi = 3.14` rather than `static const double pi = 3.14`. Adding the static forces it to have actual storage which then has to be accessed to get the number, rather than being able to insert the number where it is evaluated into the assembly; this needlessly slows your code down for no gain. – Jack Aidley Jun 02 '15 at 13:21
  • 1
    Although, that might be different in C and C++, come to think of it. – Jack Aidley Jun 02 '15 at 13:23
  • @JackAidley: I have not heard about that. Can you provide some documenting evidence? I would actually expect a `static const` to be _more likely_ to get inlined everywhere. – Lightness Races in Orbit Jun 02 '15 at 13:24
  • @JackAidley That's nonsense, variables declared at file scope always have static storage duration, no matter if they are `static` or not. Therefore all file scope variables _should_ be declared as `static` if no other file needs them, to prevent them from cluttering down the global namespace. Local variables is another matter, declaring them as static will indeed move them to `.data` section rather than getting pushed on the stack. But of course the C standard does not require nor guarantee that. – Lundin Jun 02 '15 at 13:32
  • @JackAidley, you shouldn't _always_ write `const` instead of `static const`. I will edit my post for a counterexample. – utnapistim Jun 02 '15 at 13:35
  • @Lundin: `const` variables defined with no scope specifier don't have global scope anyway (in C++, I'm not sure about C), adding `static` makes no difference to that. – Jack Aidley Jun 02 '15 at 13:47
  • @utnapistim: Ah, `static` and it's wildly different set of meanings! Yes, local `static` can be appropriate in some circumstances. – Jack Aidley Jun 02 '15 at 13:48
  • 2
    @JackAidley Eh... okay so this is why all "C/C++" questions should be closed as unclear. Just found this in C++03: "`Change: A name of file scope that is explicitly declared const, and not explicitly declared extern, has internal linkage, while in C it would have external linkage`". So this is quite different between the two languages. – Lundin Jun 02 '15 at 13:57
1

According to the "father" of C++, Stroustroup, defining constants using macros should be avoided.

The biggest Problems when using macros as constants include

  • Macros override all occurrences in the code. e.g. also variable definitions. This may result in compile Errors or undefined behavior.
  • Macros make the code very difficult to read and understand because the complexity of a macro can be hidden in a Header not clearly visible to the programmer
Thomas Sparber
  • 2,827
  • 2
  • 18
  • 34
  • 3
    In my opinon the father of c++ is the reason for a very ugly language to exist, I am going to read what you posted as a link which is not an answer. – Iharob Al Asimi Jun 02 '15 at 13:09
  • 1
    I agree that the source is questionable... but even if it wasn't, link-only answers are discouraged at SO and will get deleted. – Lundin Jun 02 '15 at 13:20