#define vs const

Started by
9 comments, last by _paf 11 years, 10 months ago
Some time ago, i read somewhere (i think it was on an old C++ book, not sure) that one should use const instead of #define, the reason being that #define is a preprocessor directive, and if for some reason the compiler ignored it, error would occur.

Now maybe at the time, compilers weren't very safe, so this line of thought was understandable (i think).

But nowadays, i think it isn't so. Let me explain:

Defining a const variable (i know, const variable doesn't sound too good), ensures that variable always has the same value (or in case of pointers a 'ex. const <type> * const <name>', as in "constant pointer, constant value" (please correct me if i'm wrong). However it uses memory to declare this variable, and it works like any other variable (if somewhere in your program there's a reference to this const variable, the program will fetch it's value from memory.

On the other hand, #define is a preprocessor directive, so no memory will be used for that value, and when a defined value is found in a program, it will be replaced by the value at compile time, not run time.

So, i'm i right to think that #define is better than const, or am i wrong?

Post your thoughts, and do correct me if i'm wrong.

Thanks in advance.
Advertisement
For modern compilers, there's really little difference with regard to the code produced. They will mangle the expressions anyway.
Furthermore, let's say they really need to be allocated and you have like 1024 constants. I'm afraid most people would get crazy really soon but... you're trashing how much? 4 KiB. No one is going to have a problem. You're torturing yourself with a problem that might be relevant on a 100 Mhz 80486. I sincerely hope you don't own an i7.

[font=courier new,courier,monospace]#define[/font] is extremely more powerful than [font=courier new,courier,monospace]const[/font] but for the purpose of this discussion, we will suppose you can only use them to define constants.
To that regard, neither guarantees the identifier will be associated with the same value depending on the order in which source files are compiled.
Sure define is more problematic as it can be undefined and re-defined again. So not only we're not really sure what it holds, we cannot even take for granted it holds the intended value. At least [font=courier new,courier,monospace]const [/font]cannot be redeclared.

Personally I don't see why you should bother. Use [font=courier new,courier,monospace]const [/font]whathever possible. In most cases, this means use define to allocate constant-sized arrays inside structs ... and little else.

Previously "Krohm"


However it uses memory to declare this variable, and it works like any other variable (if somewhere in your program there's a reference to this const variable, the program will fetch it's value from memory.

The constant will only be placed into memory if you take its address or pass it by reference. For example:

#include <stdio.h>

const int answer = 42;

int main()
{
printf("%d\n", answer);
}

turns into:

.file "floating.c"
.section .rodata.str1.1,"aMS",@progbits,1
.LC0:
.string "%d\n"
.text
.globl main
.type main, @function
main:
.LFB31:
.cfi_startproc
subq $8, %rsp
.cfi_def_cfa_offset 16
movl $42, %edx
movl $.LC0, %esi
movl $1, %edi
movl $0, %eax
call __printf_chk
movl $0, %eax
addq $8, %rsp
.cfi_def_cfa_offset 8
ret
.cfi_endproc
.LFE31:
.size main, .-main
.ident "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3"
.section .note.GNU-stack,"",@progbits

Note how the constant is placed directly into the code, and the variable answer got optimized away.
Using const you can have constants local to a function, namespace, class.. You can't do that using a #define and it can be very useful to have local constants.

Some time ago, i read somewhere (i think it was on an old C++ book, not sure) that one should use const instead of #define, the reason being that #define is a preprocessor directive, and if for some reason the compiler ignored it, error would occur.


The compiler wouldn't 'ignore' it because the compiler never saw it and there in lies the problem.

A #define is nothing more than a global text replace operation.
If you #define foo 1 then everywhere the preprocessor sees foo it replaces it with 1 with no concerns about scope nor context. This can lead to all manner of problems a classic example being the 'max' macro in the Windows headers.

If you include windows.h and then go on to try to use std::max() you'll get some lovely compiler errors spat out at you because 'max' is a #define provided by the windows.h header (or a decendant thereof) which replaces the 'max' portion of 'std::max' with some existing code.

This lack of scope is a double edged sword; on the one hand it does allow you to do global text replacement operations, on the other hand it stomps all over scope and has no regard for context.

In short; for constant variable declarations you should perfer the various C++ methods for declaring such things (const and constexpr in C++11), rather than #define, as those respect scope and are seen by the compiler.
In C++, using preprocessor string substitution ([font=courier new,courier,monospace]#define[/font]) pollutes the global namespace. This can have serious consequences in larger projects.

Using [font=courier new,courier,monospace]const[/font] and [font=courier new,courier,monospace]constexpr[/font] in C++ allows the compiler's optimizer much greater leeway since it better knows data flows and can perform folding and hoisting to a much higher degree. This can have serious benefit in larger projects.

Using [font=courier new,courier,monospace]const[/font] values rather than preprocessor string substitution allows the programmer to take advantage of the strong typing of the C++ language rather than the weak typing of the C language. It helps you eliminate entire categories of hard-to-track-down bugs.

Stephen M. Webb
Professional Free Software Developer

There are cases where memory allocation from const may be a problem, and that is when it is done inside a class.

Don't forget the possibility to use "enum". This works excellently when declared inside classes.
[size=2]Current project: Ephenation.
[size=2]Sharing OpenGL experiences: http://ephenationopengl.blogspot.com/

Don't forget the possibility to use "enum". This works excellently when declared inside classes.

Or namespaces, if they don't belong in a class.

Bregma pretty much said what I wanted to say. If you use [font=courier new,courier,monospace]#define[/font]s to define constants, I hate you. Just use a friggin' [font=courier new,courier,monospace]const[/font]s or [font=courier new,courier,monospace]enum[/font]s like you should be. Macros pollute the global namespace, which is completely unnecessary and can cause serious headaches when you have to mangle your own code just to work around someone else's laziness/stupidity. Also, their type is not explicit, and so you also don't get the benefit (or at least not as much of the benefit) of type safety, which is one of C++'s best points.

Your concern about memory is unfounded. They can (and should) be completely optimized away by the compiler. And if they're not, it's for a good reason. Trust your compiler.

Also, your comparison between const and macros fails. You mention storing a reference to a const (in which case, yes, the compiler will probably preserve that variable in memory), which isn't possible to do with a macro. You're comparing apples and oranges.

*note: I'm talking about compile-time consts here.
[size=2][ I was ninja'd 71 times before I stopped counting a long time ago ] [ f.k.a. MikeTacular ] [ My Blog ] [ SWFer: Gaplessly looped MP3s in your Flash games ]
Macros pollute the global namespace, which is completely unnecessary and can cause serious headaches when you have to mangle your own code just to work around someone else's laziness/stupidity.


No, they don't pollute any namespaces. The exist BEFORE all namespaces have even been determined. If it was just polluting the global namespace you could get around them, but since it comes before any namespace even exists, you're SOL.

In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.


[quote name='Cornstalks' timestamp='1339255957' post='4947670']Macros pollute the global namespace, which is completely unnecessary and can cause serious headaches when you have to mangle your own code just to work around someone else's laziness/stupidity.


No, they don't pollute any namespaces. The exist BEFORE all namespaces have even been determined. If it was just polluting the global namespace you could get around them, but since it comes before any namespace even exists, you're SOL.
[/quote]
Yes, that's more technically correct. What's the best way to state that though/what's it technically called? I wasn't sure of how to name it, and "polluting the global namespace" is what came to mind, which I know is a misnomer.
[size=2][ I was ninja'd 71 times before I stopped counting a long time ago ] [ f.k.a. MikeTacular ] [ My Blog ] [ SWFer: Gaplessly looped MP3s in your Flash games ]

This topic is closed to new replies.

Advertisement