The question is whether macros or constant variable declarations are preferable when wanting to name a constant. For example, if you want to name the constant pi, you have two choices:
const float PI 3.14
#define PI 3.14
The former defines an float variable PI with a const type (we learned what that means). The latter is a preprocessor directive that defines a macro PI. A preprocessor that runs before the compiler textually substitutes all mentions of PI with 3.14, so the compiler just sees 3.14 at all relevant places. There are arguments for using either so there is no short yes or no answer.
A constant variable follows scoping rules, a #define macro does not. For example, see the below example of two constant definitions, in either of the scenarios: Using const:
const float MAGIC 1.2; void foo() { const float MAGIC 1; printf("%f\n", MAGIC); // 1 } void bar() { printf("%f\n", MAGIC); // 1.2 }
Using #define:
#define MAGIC 1.2; void foo() { #define MAGIC 1; printf("%f\n", MAGIC); // 1 } void bar() { printf("%f\n", MAGIC); // 1 ??? The second define overrode the first one. }
A constant variable is type safe, a #define macro is not. For example, suppose you wanted to change a constant "MAGIC" from 1.2 to 1, in either of the scenarios: Using const:
const float MAGIC 1.2; float num = MAGIC / 2; // Was 0.6
to
const float MAGIC 1; float num = MAGIC / 2; // Is now 0.5
Using #define:
#define MAGIC 1.2 float num = MAGIC / 2; // Was 0.6
to
#define MAGIC 1 float num = MAGIC / 2; // Is now 0 (int / int = int)
See the problem? As I said in class, you should leverage the type system to your advantage wherever you can.
A constant variable requires a storage location, a #define macro does not. A constant variable is still technically a variable so it will take memory storage space, whereas a #defined constant will not. If you are working in a very memory resource constrained environment, such as in embedded systems, this may matter to you.
A constant variable requires initialization, a #define macro does not. If you have a constant variable in function scope, it may have to get initialized every time the function is called.
The reality is that modern compilers are usually smart enough to perform optimizations to remove the storage locations and initializations required for constant variables, by substituting all mentions of the constant variables by constant numbers (a.k.a. constant propagation). So unless you are working on embedded systems or real time systems where every byte or every nano second counts (and you are also worried that the compiler will not be able to optimize away your constant variable), use of constant variables are recommended over macros for the sake of safety and maintainability. These cases are rare.
A similar argument can be made about macros (with arguments) vs. functions. Macros can be faster than functions in that it does not incur function call overhead (setting up the stack etc.) on a use. But then again, there are compiler optimizations that can optimize away the overhead such as inlining. So functions are usually preferred over macros because it leads to fewer bugs due to scoping and typing issues.
But there are certain macros which cannot be replaced with a simple constant variable or a function (see slide using concatenation in "Practical C Issues"). In these cases macros are indispensable. In the end, it is a matter of coding style. But the consensus seems to be that a coding style that relies too much on macros tend to have more bugs.