4

The macro #define MAX 80 is equivalent to const int MAX = 80; Both are constant and cannot be modified.

Isn't it better to use the macro instead of the constant integer? The constant integer takes memory. The macro's name is replaced by its value by the pre-processor, right? So it wouldn't take memory.

Why would I use const int rather than the macro?

Davlog
  • 2,162
  • 8
  • 36
  • 60
  • The `const` is type safe(er), gives better errors, and can be better optimised by the compiler. – sje397 Jun 17 '13 at 13:34
  • 6
    "The constant integer takes memory. The macro's name is replaced by its value by the pre-processor, right? So it wouldn't take memory. " Well the macro value will have to be in the asm somewhere so it will definitely take memory. – drescherjm Jun 17 '13 at 13:35

4 Answers4

13

Reason #1: Scoping. Macros totally ignore scope.

namespace SomeNS {

enum Functor {
  MIN = 0
  , AVG = 1
  , MAX = 2
};

}

If the above code happens to be included in a file after the definition of the MAX macro, it will happily get preprocessed into 80 = 2, and fail compiling spectacularly.

Additionally, const variables are type safe, can be safely initialised with constant expressions (without need for parentheses) etc.

Also note that when the compiler has access to the const variable's definition when using it, it's allowed to "inline" its value. So if you never take its address, it does no even need not take up space.

Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
  • The com,piler is allowed to "inline" the const's value even if you do take its address – Jonathan Wakely Jun 17 '13 at 13:45
  • 1
    "and fail compiling spectacularly" yes, but what you have to be afraid of is code that still compiles when a macro is substituted where you didn't intend it to be. – Daniel Fischer Jun 17 '13 at 13:46
  • 2
    There is one subtle difference: say that in a header, you have a class which contains an `std::vector v;`, and an inline function which does `v.push_back( MAX );`. If `MAX` is a macro, this is fine, but if `MAX` is an `int const`, it's undefined behavior (although I've never heard of an implementation where it failed). – James Kanze Jun 17 '13 at 13:47
  • @JonathanWakely You're right indeed, thanks. Updated. – Angew is no longer proud of SO Jun 17 '13 at 13:50
  • @JamesKanze Why is it UB? – Angew is no longer proud of SO Jun 17 '13 at 13:52
  • 3
    @Agnew Because it is a violation of the one definition rule. For things that can be defined in multiple translation units (in headers), the standard requires that they consist of identical token sequences _and_ that each name, when looked up, binds to the same entity (except if the name refers to a `const` _and_ there is an immediate lvalue to rvalue conversion). In `int const`, the const implies internal linkage, which means a different entity in each translation unit. – James Kanze Jun 17 '13 at 14:08
  • @JamesKanze Wow, this consequence of const-implies-static would probably never occur to me. Thanks for the tip. – Angew is no longer proud of SO Jun 17 '13 at 14:21
  • @Angew Just to be clear: it's more a formality than anything else. The conversion to rvalue will occur at some point, and in practice, all that matters is the actual value. But it might be worth keeping in mind if you're trying to do something more complicated, where you end up comparing the address of the lvalue with the address of the constant (which will be different in different translation units). – James Kanze Jun 17 '13 at 15:40
4

There are a few reasons actually :

  • Scoping : you can't define a scope for a macro. It is present at global scope, period. Thus you can't have class-specific constants, you can't have private constants, etc. Also, you could end up with name collision, if you end up declaring something with the same name of a macro that you don't even know exists (in some lib/header you included f.e.)

  • Debugging : as the preprocessor just replaces instances of the macro with its value, it can become tricky to know why you got an error with a specific value (or just a specific behavior that you didn't expect...) . You have to remember where this value comes from. It is even more important in the case of reusable code, as you can even don't understand where does a value comes from, if it has been defined as a macro in a header you didn't write (thus it's not very good to do this yourself)

  • Adresses : a const variable is, well, a variable. It means notably that you can pass its adress around (when const pointers or const reference are needed), but you can't with macro

  • Type safety : you can specify a type for a const variable, something you can't for a macro.

As a general rule, I'd say that (in my opinion) you should avoid #define directives when you have a clear alternative (i.e. const variables, enums, inlines).

JBL
  • 12,588
  • 4
  • 53
  • 84
3

The thing is they aren't the same. The macro is just text substitution by the preprocessor while the const is a normal variable.

If someone ever tries to shadow MAX within a function (like const in MAX = 32;) they get a really weird error message when MAX is a macro.

In C++ the language-idiomatic approach is to use constants rather than macros. Trying to save a few bytes of memory (if it even saves them) doesn't seem worth the cost in readability.

Mark B
  • 95,107
  • 10
  • 109
  • 188
2

1) Debugging is the main one for me. It's difficult for a debugger to resolve MAX to the value at run time, but it can do it with the const int version.

2) You don't get any type information with #define. If you're using a template-based function; say std::max where your other datum is a const int then the macro version will fail but the const int version will not. To work around that you'd have to use #define MAX 80U which is ugly.

3) You cannot control scoping with #define; it will apply to the whole compilation unit following the #define statement.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
  • 1
    Are you sure that the `std::max` will fail to compile if the other value is an `int const`? `80` has type `int` as well (and top level const is ignored). If the other value is _not_ an `int`, it will fail to compile, but that is true in both cases. – James Kanze Jun 17 '13 at 13:45