#define is a preprocessor directive used to define macros. It allows programmers to assign a name to a constant value or a piece of code. Before compilation, the preprocessor replaces occurrences of the macro name with its defined replacement text.
- #define is commonly used to define symbolic constants and function-like macros.
- It performs text substitution during preprocessing, before the actual compilation.
- Unlike variables and functions, macros do not provide type checking.
Example: Program to define a constant value and calculate the area of a circle using #define.
#include <iostream>
using namespace std;
#define PI 3.14159
int main(){
double radius = 5;
double area = PI * radius * radius;
cout << "Area of Circle: " << area;
return 0;
}
Output
Area of Circle: 78.5397
Explanation:
- The #define PI 3.14159 directive defines a macro named PI.
- During preprocessing, each occurrence of PI is replaced with 3.14159.
- The program calculates the area using the formula PI * radius * radius.
Syntax
The #define directive is written using the following syntax:
#define MACRO_NAME replacement_text
Here:
- #define: Preprocessor directive used to define a macro.
- MACRO_NAME: Name of the macro, generally written in uppercase.
- replacement_text: Value or code that replaces the macro name.
Example:
#define PI 3.14159
Whenever PI appears in the source code in a context where the macro is expanded, the preprocessor replaces it with 3.14159.
Note: The #define directive does not require a semicolon (;) at the end.
Types of Macros
The #define directive is mainly used to create two types of macros.
1. Object-Like Macros
Object-like macros define a name that represents a fixed value or replacement text. They are commonly used for constants.
#include <iostream>
using namespace std;
#define MAX_SIZE 100
int main(){
cout << "Maximum Size: " << MAX_SIZE;
return 0;
}
Output
Maximum Size: 100
Explanation: The preprocessor replaces MAX_SIZE with 100 before compilation.
2. Function-Like Macros
Function-like macros accept arguments and replace them with a predefined expression. They resemble functions but are expanded by the preprocessor.
Syntax:
#define MACRO_NAME(parameters) expression
#include <iostream>
using namespace std;
#define SQUARE(x) ((x) * (x))
int main(){
int num = 5;
cout << "Square: " << SQUARE(num);
return 0;
}
Output
Square: 25
Explanation: The macro SQUARE(num) expands to ((num) * (num)), which calculates the square of the number.
Note: Parentheses are important in function-like macros to avoid operator-precedence issues. However, macros can still evaluate their arguments multiple times, so inline functions are generally safer.
How Does #define Work
The #define directive works during the preprocessing stage, before the compiler translates C++ code into machine code.
- The preprocessor reads the #define directive and stores the macro definition.
- When it encounters the macro name, it expands it using the defined replacement text.
- The resulting code is then passed to the compiler.
For example:
Original code:
#define VALUE 10
int result = VALUE * 2;
After preprocessing:
int result = 10 * 2;
The compiler processes the expanded code.
Conditional Compilation Using #define
The #define directive can also be used with conditional preprocessing directives such as #ifdef, #ifndef, and #endif. These directives allow specific sections of code to be included or excluded based on whether a macro is defined.
#include <iostream>
using namespace std;
#define DEBUG
int main(){
#ifdef DEBUG
cout << "Debug mode is enabled" << endl;
#endif
cout << "Program is running";
return 0;
}
Output
Debug mode is enabled Program is running
Explanation:
- #define DEBUG defines the DEBUG macro without assigning a value.
- #ifdef DEBUG checks whether the macro is defined.
- Since DEBUG is defined, the statement inside the conditional block is included during compilation.
Undefining a Macro Using #undef
The #undef directive removes an existing macro definition. After it is undefined, the macro name is no longer replaced by the preprocessor unless it is defined again.
#include <iostream>
using namespace std;
#define VALUE 50
int main(){
cout << VALUE << endl;
#undef VALUE
#define VALUE 100
cout << VALUE;
return 0;
}
Output
50 100
Explanation: Initially, VALUE is defined as 50. After #undef, it is redefined as 100, so the second output displays the updated value.
Advantages of #define
- Allows symbolic names to replace repeated literal values.
- Supports conditional compilation for debugging and platform-specific code.
- Can define reusable code fragments through function-like macros.
Limitations of #define
- Does not provide type checking or respect normal C++ scope rules.
- Function-like macros may produce unexpected results due to operator precedence or repeated argument evaluation.
- Macro expansion can make debugging and error messages more difficult to understand.